mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
log: Add structured logging (#1171)
* basic implementation * log: deprecate duplicate function, add tests and refine calls. * linter: fixes * linter: update struct * linter and new type * log tests: update to not lint issue * linter: stop complaining please * glorious: nits * log: rm comment code * glorious: nits * glorious: nits * glorious: nits * glorious: nits missed --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
@@ -221,22 +221,22 @@ func (c *Config) validateCurrencySettings() error {
|
||||
|
||||
// PrintSetting prints relevant settings to the console for easy reading
|
||||
func (c *Config) PrintSetting() {
|
||||
log.Info(common.Config, common.CMDColours.H1+"------------------Backtester Settings------------------------"+common.CMDColours.Default)
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------Strategy Settings--------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H1+"------------------Backtester Settings------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------Strategy Settings--------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Strategy: %s", c.StrategySettings.Name)
|
||||
if len(c.StrategySettings.CustomSettings) > 0 {
|
||||
log.Info(common.Config, "Custom strategy variables:")
|
||||
log.Infoln(common.Config, "Custom strategy variables:")
|
||||
for k, v := range c.StrategySettings.CustomSettings {
|
||||
log.Infof(common.Config, "%s: %v", k, v)
|
||||
}
|
||||
} else {
|
||||
log.Info(common.Config, "Custom strategy variables: unset")
|
||||
log.Infoln(common.Config, "Custom strategy variables: unset")
|
||||
}
|
||||
log.Infof(common.Config, "Simultaneous Signal Processing: %v", c.StrategySettings.SimultaneousSignalProcessing)
|
||||
log.Infof(common.Config, "USD value tracking: %v", !c.StrategySettings.DisableUSDTracking)
|
||||
|
||||
if c.FundingSettings.UseExchangeLevelFunding && c.StrategySettings.SimultaneousSignalProcessing {
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------Funding Settings---------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------Funding Settings---------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Use Exchange Level Funding: %v", c.FundingSettings.UseExchangeLevelFunding)
|
||||
if c.DataSettings.LiveData != nil && c.DataSettings.LiveData.RealOrders {
|
||||
log.Infof(common.Config, "Funding levels will be set by the exchange")
|
||||
@@ -297,12 +297,12 @@ func (c *Config) PrintSetting() {
|
||||
log.Infof(common.Config, "Can use exchange defined order execution limits: %+v", c.CurrencySettings[i].CanUseExchangeLimits)
|
||||
}
|
||||
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------Portfolio Settings-------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------Portfolio Settings-------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Buy rules: %+v", c.PortfolioSettings.BuySide)
|
||||
log.Infof(common.Config, "Sell rules: %+v", c.PortfolioSettings.SellSide)
|
||||
log.Infof(common.Config, "Leverage rules: %+v", c.PortfolioSettings.Leverage)
|
||||
if c.DataSettings.LiveData != nil {
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------Live Settings------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------Live Settings------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType)
|
||||
log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval)
|
||||
log.Infof(common.Config, "Using real orders: %v", c.DataSettings.LiveData.RealOrders)
|
||||
@@ -313,20 +313,20 @@ func (c *Config) PrintSetting() {
|
||||
}
|
||||
}
|
||||
if c.DataSettings.APIData != nil {
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------API Settings-------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------API Settings-------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType)
|
||||
log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval)
|
||||
log.Infof(common.Config, "Start date: %v", c.DataSettings.APIData.StartDate.Format(gctcommon.SimpleTimeFormat))
|
||||
log.Infof(common.Config, "End date: %v", c.DataSettings.APIData.EndDate.Format(gctcommon.SimpleTimeFormat))
|
||||
}
|
||||
if c.DataSettings.CSVData != nil {
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------CSV Settings-------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------CSV Settings-------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType)
|
||||
log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval)
|
||||
log.Infof(common.Config, "CSV file: %v", c.DataSettings.CSVData.FullPath)
|
||||
}
|
||||
if c.DataSettings.DatabaseData != nil {
|
||||
log.Info(common.Config, common.CMDColours.H2+"------------------Database Settings--------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Config, common.CMDColours.H2+"------------------Database Settings--------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Config, "Data type: %v", c.DataSettings.DataType)
|
||||
log.Infof(common.Config, "Interval: %v", c.DataSettings.Interval)
|
||||
log.Infof(common.Config, "Start date: %v", c.DataSettings.DatabaseData.StartDate.Format(gctcommon.SimpleTimeFormat))
|
||||
|
||||
@@ -95,7 +95,7 @@ func (bt *BackTest) RunLive() error {
|
||||
go func() {
|
||||
err = bt.liveCheck()
|
||||
if err != nil {
|
||||
log.Error(common.LiveStrategy, err)
|
||||
log.Errorln(common.LiveStrategy, err)
|
||||
}
|
||||
bt.wg.Done()
|
||||
}()
|
||||
@@ -153,7 +153,7 @@ func (bt *BackTest) ExecuteStrategy(waitForOfflineCompletion bool) error {
|
||||
case waitForOfflineCompletion && !liveTesting:
|
||||
err = bt.Run()
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
return bt.Stop()
|
||||
case !waitForOfflineCompletion && liveTesting:
|
||||
@@ -162,11 +162,11 @@ func (bt *BackTest) ExecuteStrategy(waitForOfflineCompletion bool) error {
|
||||
go func() {
|
||||
err = bt.Run()
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
err = bt.Stop()
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func (bt *BackTest) Run() error {
|
||||
}
|
||||
if doubleNil {
|
||||
if bt.verbose {
|
||||
log.Info(common.Backtester, "No new data on second check")
|
||||
log.Infoln(common.Backtester, "No new data on second check")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -237,7 +237,7 @@ func (bt *BackTest) Run() error {
|
||||
doubleNil = false
|
||||
err := bt.handleEvent(ev)
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
if !bt.hasProcessedAnEvent {
|
||||
bt.hasProcessedAnEvent = true
|
||||
@@ -283,7 +283,7 @@ func (bt *BackTest) handleEvent(ev common.Event) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(common.LiveStrategy, result)
|
||||
log.Infoln(common.LiveStrategy, result)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("handleEvent %w %T received, could not process",
|
||||
@@ -359,7 +359,7 @@ func (bt *BackTest) processSimultaneousDataEvents() error {
|
||||
case errors.Is(err, gctorder.ErrPositionLiquidated):
|
||||
return nil
|
||||
default:
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
}
|
||||
dataEvents = append(dataEvents, dataHolders[i])
|
||||
@@ -524,7 +524,7 @@ func (bt *BackTest) processFillEvent(ev fill.Event, funds funding.IFundReleaser)
|
||||
}
|
||||
holding, err := bt.Portfolio.ViewHoldingAtTimePeriod(ev)
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
err = bt.Statistic.AddHoldingsForTime(holding)
|
||||
if err != nil {
|
||||
|
||||
@@ -86,7 +86,7 @@ func StartRPCServer(server *GRPCServer) error {
|
||||
|
||||
go func() {
|
||||
if err = s.Serve(lis); err != nil {
|
||||
log.Error(log.GRPCSys, err)
|
||||
log.Errorln(log.GRPCSys, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
@@ -133,7 +133,7 @@ func (s *GRPCServer) StartRPCRESTProxy() error {
|
||||
}
|
||||
}()
|
||||
|
||||
log.Debug(log.GRPCSys, "GRPC proxy server started!")
|
||||
log.Debugln(log.GRPCSys, "GRPC proxy server started!")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ func (d *dataChecker) Start() error {
|
||||
if err != nil {
|
||||
stopErr := d.SignalStopFromError(err)
|
||||
if stopErr != nil {
|
||||
log.Error(common.LiveStrategy, stopErr)
|
||||
log.Errorln(common.LiveStrategy, stopErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -118,7 +118,7 @@ func (d *dataChecker) SignalStopFromError(err error) error {
|
||||
if !atomic.CompareAndSwapUint32(&d.started, 1, 0) {
|
||||
return engine.ErrSubSystemNotStarted
|
||||
}
|
||||
log.Error(common.LiveStrategy, err)
|
||||
log.Errorln(common.LiveStrategy, err)
|
||||
d.shutdownErr <- true
|
||||
return nil
|
||||
}
|
||||
@@ -399,7 +399,7 @@ func (d *dataChecker) FetchLatestData() (bool, error) {
|
||||
err = d.UpdateFunding(false)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
log.Error(common.LiveStrategy, err)
|
||||
log.Errorln(common.LiveStrategy, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,7 +783,7 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange,
|
||||
defer func() {
|
||||
stopErr := bt.databaseManager.Stop()
|
||||
if stopErr != nil {
|
||||
log.Error(common.Setup, stopErr)
|
||||
log.Errorln(common.Setup, stopErr)
|
||||
}
|
||||
}()
|
||||
resp, err = loadDatabaseData(cfg, exch.GetName(), fPair, a, dataType, isUSDTrackingPair)
|
||||
|
||||
@@ -169,7 +169,7 @@ func CalculateBiggestValueAtTimeDrawdown(closePrices []ValueAtTime, interval gct
|
||||
}
|
||||
intervals, err := gctkline.CalculateCandleDateRanges(highestTime, lowestTime, interval, 0)
|
||||
if err != nil {
|
||||
log.Error(common.CurrencyStatistics, err)
|
||||
log.Errorln(common.CurrencyStatistics, err)
|
||||
}
|
||||
drawdownPercent := decimal.Zero
|
||||
if highestPrice.GreaterThan(decimal.Zero) {
|
||||
|
||||
@@ -36,13 +36,13 @@ func addReason(reason, msg string) string {
|
||||
|
||||
// PrintTotalResults outputs all results to the CMD
|
||||
func (s *Statistic) PrintTotalResults() {
|
||||
log.Info(common.Statistics, common.CMDColours.H1+"------------------Strategy-----------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H1+"------------------Strategy-----------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Statistics, "Strategy Name: %v", s.StrategyName)
|
||||
log.Infof(common.Statistics, "Strategy Nickname: %v", s.StrategyNickname)
|
||||
log.Infof(common.Statistics, "Strategy Goal: %v\n\n", s.StrategyGoal)
|
||||
|
||||
log.Info(common.Statistics, common.CMDColours.H2+"------------------Total Results------------------------------"+common.CMDColours.Default)
|
||||
log.Info(common.Statistics, common.CMDColours.H3+"------------------Orders-------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H2+"------------------Total Results------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H3+"------------------Orders-------------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Statistics, "Total buy orders: %v", convert.IntToHumanFriendlyString(s.TotalBuyOrders, ","))
|
||||
log.Infof(common.Statistics, "Total sell orders: %v", convert.IntToHumanFriendlyString(s.TotalSellOrders, ","))
|
||||
log.Infof(common.Statistics, "Total long orders: %v", convert.IntToHumanFriendlyString(s.TotalLongOrders, ","))
|
||||
@@ -50,7 +50,7 @@ func (s *Statistic) PrintTotalResults() {
|
||||
log.Infof(common.Statistics, "Total orders: %v\n\n", convert.IntToHumanFriendlyString(s.TotalOrders, ","))
|
||||
|
||||
if s.BiggestDrawdown != nil {
|
||||
log.Info(common.Statistics, common.CMDColours.H3+"------------------Biggest Drawdown-----------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H3+"------------------Biggest Drawdown-----------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Statistics, "Exchange: %v Asset: %v Currency: %v", s.BiggestDrawdown.Exchange, s.BiggestDrawdown.Asset, s.BiggestDrawdown.Pair)
|
||||
log.Infof(common.Statistics, "Highest Price: %s", convert.DecimalToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.Highest.Value, 8, ".", ","))
|
||||
log.Infof(common.Statistics, "Highest Price Time: %v", s.BiggestDrawdown.MaxDrawdown.Highest.Time)
|
||||
@@ -61,7 +61,7 @@ func (s *Statistic) PrintTotalResults() {
|
||||
log.Infof(common.Statistics, "Drawdown length: %v candles\n\n", convert.IntToHumanFriendlyString(s.BiggestDrawdown.MaxDrawdown.IntervalDuration, ","))
|
||||
}
|
||||
if s.BestMarketMovement != nil && s.BestStrategyResults != nil {
|
||||
log.Info(common.Statistics, common.CMDColours.H4+"------------------Orders----------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H4+"------------------Orders----------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.Statistics, "Best performing market movement: %v %v %v %v%%", s.BestMarketMovement.Exchange, s.BestMarketMovement.Asset, s.BestMarketMovement.Pair, convert.DecimalToHumanFriendlyString(s.BestMarketMovement.MarketMovement, 2, ".", ","))
|
||||
log.Infof(common.Statistics, "Best performing strategy movement: %v %v %v %v%%\n\n", s.BestStrategyResults.Exchange, s.BestStrategyResults.Asset, s.BestStrategyResults.Pair, convert.DecimalToHumanFriendlyString(s.BestStrategyResults.StrategyMovement, 2, ".", ","))
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (s *Statistic) PrintTotalResults() {
|
||||
// rather than separated by exchange, asset and currency pair, it's
|
||||
// grouped by time to allow a clearer picture of events
|
||||
func (s *Statistic) PrintAllEventsChronologically() {
|
||||
log.Info(common.Statistics, common.CMDColours.H1+"------------------Events-------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.H1+"------------------Events-------------------------------------"+common.CMDColours.Default)
|
||||
var errs error
|
||||
var results []eventOutputHolder
|
||||
for _, exchangeMap := range s.ExchangeAssetPairStatistics {
|
||||
@@ -119,13 +119,13 @@ func (s *Statistic) PrintAllEventsChronologically() {
|
||||
})
|
||||
for i := range results {
|
||||
for j := range results[i].Events {
|
||||
log.Info(common.Statistics, results[i].Events[j])
|
||||
log.Infoln(common.Statistics, results[i].Events[j])
|
||||
}
|
||||
}
|
||||
if errs != nil {
|
||||
log.Info(common.Statistics, common.CMDColours.Error+"------------------Errors-------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.Statistics, common.CMDColours.Error+"------------------Errors-------------------------------------"+common.CMDColours.Default)
|
||||
for err := errors.Unwrap(errs); err != nil; err = errors.Unwrap(errs) {
|
||||
log.Error(common.Statistics, err.Error())
|
||||
log.Errorln(common.Statistics, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,17 +239,17 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.
|
||||
|
||||
log.Infof(common.CurrencyStatistics, "%s Total orders: %s", sep, convert.IntToHumanFriendlyString(c.TotalOrders, ","))
|
||||
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H2+"------------------Max Drawdown-------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H2+"------------------Max Drawdown-------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.CurrencyStatistics, "%s Highest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value, 8, ".", ","), c.MaxDrawdown.Highest.Time)
|
||||
log.Infof(common.CurrencyStatistics, "%s Lowest Price of drawdown: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Lowest.Value, 8, ".", ","), c.MaxDrawdown.Lowest.Time)
|
||||
log.Infof(common.CurrencyStatistics, "%s Calculated Drawdown: %s%%", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.DrawdownPercent, 8, ".", ","))
|
||||
log.Infof(common.CurrencyStatistics, "%s Difference: %s", sep, convert.DecimalToHumanFriendlyString(c.MaxDrawdown.Highest.Value.Sub(c.MaxDrawdown.Lowest.Value), 2, ".", ","))
|
||||
log.Infof(common.CurrencyStatistics, "%s Drawdown length: %s", sep, convert.IntToHumanFriendlyString(c.MaxDrawdown.IntervalDuration, ","))
|
||||
if !usingExchangeLevelFunding && c.TotalOrders > 1 {
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H2+"------------------Ratios------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H3+"------------------Rates-------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H2+"------------------Ratios------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H3+"------------------Rates-------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.CurrencyStatistics, "%s Compound Annual Growth Rate: %s", sep, convert.DecimalToHumanFriendlyString(c.CompoundAnnualGrowthRate, 2, ".", ","))
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H4+"------------------Arithmetic--------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H4+"------------------Arithmetic--------------------------------------------"+common.CMDColours.Default)
|
||||
if c.ShowMissingDataWarning {
|
||||
log.Infoln(common.CurrencyStatistics, "Missing data was detected during this backtesting run")
|
||||
log.Infoln(common.CurrencyStatistics, "Ratio calculations will be skewed")
|
||||
@@ -259,7 +259,7 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.
|
||||
log.Infof(common.CurrencyStatistics, "%s Information ratio: %v", sep, c.ArithmeticRatios.InformationRatio.Round(4))
|
||||
log.Infof(common.CurrencyStatistics, "%s Calmar ratio: %v", sep, c.ArithmeticRatios.CalmarRatio.Round(4))
|
||||
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H4+"------------------Geometric--------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H4+"------------------Geometric--------------------------------------------"+common.CMDColours.Default)
|
||||
if c.ShowMissingDataWarning {
|
||||
log.Infoln(common.CurrencyStatistics, "Missing data was detected during this backtesting run")
|
||||
log.Infoln(common.CurrencyStatistics, "Ratio calculations will be skewed")
|
||||
@@ -270,7 +270,7 @@ func (c *CurrencyPairStatistic) PrintResults(e string, a asset.Item, p currency.
|
||||
log.Infof(common.CurrencyStatistics, "%s Calmar ratio: %v", sep, c.GeometricRatios.CalmarRatio.Round(4))
|
||||
}
|
||||
|
||||
log.Info(common.CurrencyStatistics, common.CMDColours.H2+"------------------Results------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.CurrencyStatistics, common.CMDColours.H2+"------------------Results------------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.CurrencyStatistics, "%s Starting Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.StartingClosePrice.Value, 8, ".", ","), c.StartingClosePrice.Time)
|
||||
log.Infof(common.CurrencyStatistics, "%s Finishing Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.EndingClosePrice.Value, 8, ".", ","), c.EndingClosePrice.Time)
|
||||
log.Infof(common.CurrencyStatistics, "%s Lowest Close Price: %s at %v", sep, convert.DecimalToHumanFriendlyString(c.LowestClosePrice.Value, 8, ".", ","), c.LowestClosePrice.Time)
|
||||
@@ -318,10 +318,10 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error {
|
||||
}
|
||||
}
|
||||
if len(spotResults) > 0 || len(futuresResults) > 0 {
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H1+"------------------Funding------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H1+"------------------Funding------------------------------------"+common.CMDColours.Default)
|
||||
}
|
||||
if len(spotResults) > 0 {
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H2+"------------------Funding Spot Item Results------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H2+"------------------Funding Spot Item Results------------------"+common.CMDColours.Default)
|
||||
for i := range spotResults {
|
||||
if spotResults[i].ReportItem.AppendedViaAPI {
|
||||
continue
|
||||
@@ -346,12 +346,12 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error {
|
||||
log.Infof(common.FundingStatistics, "%s Transfer fee: %s", sep, convert.DecimalToHumanFriendlyString(spotResults[i].ReportItem.TransferFee, 8, ".", ","))
|
||||
}
|
||||
if i != len(spotResults)-1 {
|
||||
log.Info(common.FundingStatistics, "")
|
||||
log.Infoln(common.FundingStatistics, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(futuresResults) > 0 {
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H2+"------------------Funding Futures Item Results---------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H2+"------------------Funding Futures Item Results---------------"+common.CMDColours.Default)
|
||||
for i := range futuresResults {
|
||||
if futuresResults[i].ReportItem.AppendedViaAPI {
|
||||
continue
|
||||
@@ -373,14 +373,14 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error {
|
||||
log.Infof(common.FundingStatistics, "%s Final Contract Holdings: %v %v at %v", sep, futuresResults[i].FinalHoldings.Value, futuresResults[i].ReportItem.Currency, futuresResults[i].FinalHoldings.Time)
|
||||
}
|
||||
if i != len(futuresResults)-1 {
|
||||
log.Info(common.FundingStatistics, "")
|
||||
log.Infoln(common.FundingStatistics, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
if f.Report.DisableUSDTracking {
|
||||
return nil
|
||||
}
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H2+"------------------USD Tracking Totals------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H2+"------------------USD Tracking Totals------------------------"+common.CMDColours.Default)
|
||||
sep := "USD Tracking Total |\t"
|
||||
|
||||
log.Infof(common.FundingStatistics, "%s Initial value: $%s", sep, convert.DecimalToHumanFriendlyString(f.Report.InitialFunds, 8, ".", ","))
|
||||
@@ -392,14 +392,14 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error {
|
||||
log.Infof(common.FundingStatistics, "%s Highest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.HighestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.HighestHoldingValue.Time)
|
||||
log.Infof(common.FundingStatistics, "%s Lowest funds: $%s at %v", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.LowestHoldingValue.Value, 8, ".", ","), f.TotalUSDStatistics.LowestHoldingValue.Time)
|
||||
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H3+"------------------Ratios------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H4+"------------------Rates-------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H3+"------------------Ratios------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H4+"------------------Rates-------------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infof(common.FundingStatistics, "%s Risk free rate: %s%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.RiskFreeRate.Mul(decimal.NewFromInt(100)), 2, ".", ","))
|
||||
log.Infof(common.FundingStatistics, "%s Compound Annual Growth Rate: %v%%", sep, convert.DecimalToHumanFriendlyString(f.TotalUSDStatistics.CompoundAnnualGrowthRate, 8, ".", ","))
|
||||
if f.TotalUSDStatistics.ArithmeticRatios == nil || f.TotalUSDStatistics.GeometricRatios == nil {
|
||||
return fmt.Errorf("%w missing ratio calculations", gctcommon.ErrNilPointer)
|
||||
}
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H4+"------------------Arithmetic--------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H4+"------------------Arithmetic--------------------------------------------"+common.CMDColours.Default)
|
||||
if wasAnyDataMissing {
|
||||
log.Infoln(common.FundingStatistics, "Missing data was detected during this backtesting run")
|
||||
log.Infoln(common.FundingStatistics, "Ratio calculations will be skewed")
|
||||
@@ -409,7 +409,7 @@ func (f *FundingStatistics) PrintResults(wasAnyDataMissing bool) error {
|
||||
log.Infof(common.FundingStatistics, "%s Information ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.InformationRatio.Round(4))
|
||||
log.Infof(common.FundingStatistics, "%s Calmar ratio: %v", sep, f.TotalUSDStatistics.ArithmeticRatios.CalmarRatio.Round(4))
|
||||
|
||||
log.Info(common.FundingStatistics, common.CMDColours.H4+"------------------Geometric--------------------------------------------"+common.CMDColours.Default)
|
||||
log.Infoln(common.FundingStatistics, common.CMDColours.H4+"------------------Geometric--------------------------------------------"+common.CMDColours.Default)
|
||||
if wasAnyDataMissing {
|
||||
log.Infoln(common.FundingStatistics, "Missing data was detected during this backtesting run")
|
||||
log.Infoln(common.FundingStatistics, "Ratio calculations will be skewed")
|
||||
|
||||
@@ -211,7 +211,7 @@ func (s *Statistic) AddComplianceSnapshotForTime(c *compliance.Snapshot, e commo
|
||||
// CalculateAllResults calculates the statistics of all exchange asset pair holdings,
|
||||
// orders, ratios and drawdowns
|
||||
func (s *Statistic) CalculateAllResults() error {
|
||||
log.Info(common.Statistics, "Calculating backtesting results")
|
||||
log.Infoln(common.Statistics, "Calculating backtesting results")
|
||||
s.PrintAllEventsChronologically()
|
||||
currCount := 0
|
||||
var finalResults []FinalResultsHolder
|
||||
@@ -227,7 +227,7 @@ func (s *Statistic) CalculateAllResults() error {
|
||||
}
|
||||
err = stats.CalculateResults(s.RiskFreeRate)
|
||||
if err != nil {
|
||||
log.Error(common.Statistics, err)
|
||||
log.Errorln(common.Statistics, err)
|
||||
}
|
||||
stats.FinalHoldings = last.Holdings
|
||||
stats.InitialHoldings = stats.Events[0].Holdings
|
||||
|
||||
@@ -138,7 +138,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = log.SetupGlobalLogger()
|
||||
err = log.SetupGlobalLogger("gct/backtester", false)
|
||||
if err != nil {
|
||||
fmt.Printf("Could not setup global logger. Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
@@ -218,7 +218,7 @@ func main() {
|
||||
runManager := backtest.NewTaskManager()
|
||||
|
||||
go func(c *config.BacktesterConfig) {
|
||||
log.Info(log.GRPCSys, "Starting RPC server")
|
||||
log.Infoln(log.GRPCSys, "Starting RPC server")
|
||||
var s *backtest.GRPCServer
|
||||
s, err = backtest.SetupRPCServer(c, runManager)
|
||||
err = backtest.StartRPCServer(s)
|
||||
@@ -226,7 +226,7 @@ func main() {
|
||||
fmt.Printf("Could not start RPC server. Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info(log.GRPCSys, "Ready to receive commands")
|
||||
log.Infoln(log.GRPCSys, "Ready to receive commands")
|
||||
}(btCfg)
|
||||
interrupt := signaler.WaitForInterrupt()
|
||||
log.Infof(log.Global, "Captured %v, shutdown requested\n", interrupt)
|
||||
@@ -235,7 +235,7 @@ func main() {
|
||||
var stopped []*backtest.TaskSummary
|
||||
stopped, err = runManager.StopAllTasks()
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
for i := range stopped {
|
||||
log.Infof(common.Backtester, "Task %v %v was stopped", stopped[i].MetaData.ID, stopped[i].MetaData.Strategy)
|
||||
@@ -244,13 +244,13 @@ func main() {
|
||||
var tasks []*backtest.TaskSummary
|
||||
tasks, err = runManager.List()
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
}
|
||||
for i := range tasks {
|
||||
if tasks[i].MetaData.ClosePositionsOnStop && !tasks[i].MetaData.Closed {
|
||||
err = runManager.StopTask(tasks[i].MetaData.ID)
|
||||
if err != nil {
|
||||
log.Error(common.Backtester, err)
|
||||
log.Errorln(common.Backtester, err)
|
||||
continue
|
||||
}
|
||||
log.Infof(common.Backtester, "Task %v %v was stopped", tasks[i].MetaData.ID, tasks[i].MetaData.Strategy)
|
||||
|
||||
@@ -21,7 +21,7 @@ func (d *Data) GenerateReport() error {
|
||||
if d.TemplatePath == "" || d.OutputPath == "" {
|
||||
return nil
|
||||
}
|
||||
log.Info(common.Report, "Generating report")
|
||||
log.Infoln(common.Report, "Generating report")
|
||||
err := d.enhanceCandles()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -95,7 +95,7 @@ func (d *Data) GenerateReport() error {
|
||||
defer func() {
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
log.Error(common.Report, err)
|
||||
log.Errorln(common.Report, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -104,19 +104,19 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = log.SetupGlobalLogger()
|
||||
err = log.SetupGlobalLogger("cmd/apicheck", false)
|
||||
if err != nil {
|
||||
fmt.Printf("Could not setup global logger. Error: %v.\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
configData, err = readFileData(jsonFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
testConfigData, err = readFileData(testJSONFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
usageData = testConfigData
|
||||
@@ -130,7 +130,7 @@ func main() {
|
||||
data.Repo = path
|
||||
err = addExch(exchangeName, checkType, data, false)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case htmlScrape:
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
data.Path = path
|
||||
err = addExch(exchangeName, checkType, data, false)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func main() {
|
||||
if trelloBoardName != "" {
|
||||
a, err = trelloGetBoardID()
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
trelloBoardID = a
|
||||
@@ -164,25 +164,25 @@ func main() {
|
||||
if create {
|
||||
err = createAndSet()
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
err = updateFile(backupFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = checkUpdates(jsonFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Warnln(log.Global, "This is a test update since API keys are not set.")
|
||||
err := checkUpdates(testJSONFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infoln(log.Global, "API update check completed successfully")
|
||||
|
||||
@@ -26,18 +26,18 @@ func TestMain(m *testing.M) {
|
||||
setTestVars()
|
||||
err := log.SetGlobalLogConfig(log.GenDefaultSettings())
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Infoln(log.Global, "set verbose to true for more detailed output")
|
||||
configData, err = readFileData(jsonFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
testConfigData, err = readFileData(testJSONFile)
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
usageData = testConfigData
|
||||
@@ -45,7 +45,7 @@ func TestMain(m *testing.M) {
|
||||
testExitCode := m.Run()
|
||||
err = removeTestFileVars()
|
||||
if err != nil {
|
||||
log.Error(log.Global, err)
|
||||
log.Errorln(log.Global, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(testExitCode)
|
||||
|
||||
@@ -142,7 +142,7 @@ func (c *Config) CheckClientBankAccounts() {
|
||||
err := c.BankAccounts[i].Validate()
|
||||
if err != nil {
|
||||
c.BankAccounts[i].Enabled = false
|
||||
log.Warn(log.ConfigMgr, err.Error())
|
||||
log.Warnln(log.ConfigMgr, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1047,7 +1047,7 @@ func (c *Config) CheckBankAccountConfig() {
|
||||
err := c.BankAccounts[x].Validate()
|
||||
if err != nil {
|
||||
c.BankAccounts[x].Enabled = false
|
||||
log.Warn(log.ConfigMgr, err.Error())
|
||||
log.Warnln(log.ConfigMgr, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1559,7 +1559,7 @@ func readEncryptedConfWithKey(reader *bufio.Reader, keyProvider func() ([]byte,
|
||||
var c *Config
|
||||
c, err = readEncryptedConf(bytes.NewReader(fileData), key)
|
||||
if err != nil {
|
||||
log.Error(log.ConfigMgr, "Could not decrypt and deserialise data with given key. Invalid password?", err)
|
||||
log.Errorln(log.ConfigMgr, "Could not decrypt and deserialise data with given key. Invalid password?", err)
|
||||
continue
|
||||
}
|
||||
return c, nil
|
||||
@@ -1594,7 +1594,7 @@ func (c *Config) SaveConfigToFile(configPath string) error {
|
||||
if writer != nil {
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
log.Error(log.ConfigMgr, err)
|
||||
log.Errorln(log.ConfigMgr, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -20,7 +20,7 @@ const (
|
||||
func areAPICredtionalsSet(minAllowable uint8) bool {
|
||||
if apiAccountPlanLevel != "" && apikey != "" {
|
||||
if err := c.CheckAccountPlan(minAllowable); err != nil {
|
||||
log.Warn(log.Global, "coinmarketpcap test suite - account plan not allowed for function, please review or upgrade plan to test")
|
||||
log.Warnln(log.Global, "coinmarketpcap test suite - account plan not allowed for function, please review or upgrade plan to test")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -35,7 +35,7 @@ func Event(res *withdraw.Response) {
|
||||
|
||||
exchangeUUID, err := exchangeDB.UUIDByName(res.Exchange.Name)
|
||||
if err != nil {
|
||||
log.Error(log.DatabaseMgr, err)
|
||||
log.Errorln(log.DatabaseMgr, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func GetEventByUUID(id string) (*withdraw.Response, error) {
|
||||
func GetEventsByExchange(exchange string, limit int) ([]*withdraw.Response, error) {
|
||||
exch, err := exchangeDB.UUIDByName(exchange)
|
||||
if err != nil {
|
||||
log.Error(log.DatabaseMgr, err)
|
||||
log.Errorln(log.DatabaseMgr, err)
|
||||
return nil, err
|
||||
}
|
||||
return getByColumns(generateWhereQuery([]string{"exchange_name_id"}, []string{exch.String()}, limit))
|
||||
@@ -242,7 +242,7 @@ func GetEventsByExchange(exchange string, limit int) ([]*withdraw.Response, erro
|
||||
func GetEventByExchangeID(exchange, id string) (*withdraw.Response, error) {
|
||||
exch, err := exchangeDB.UUIDByName(exchange)
|
||||
if err != nil {
|
||||
log.Error(log.DatabaseMgr, err)
|
||||
log.Errorln(log.DatabaseMgr, err)
|
||||
return nil, err
|
||||
}
|
||||
resp, err := getByColumns(generateWhereQuery([]string{"exchange_name_id", "exchange_id"}, []string{exch.String(), id}, 1))
|
||||
@@ -260,7 +260,7 @@ func GetEventsByDate(exchange string, start, end time.Time, limit int) ([]*withd
|
||||
}
|
||||
exch, err := exchangeDB.UUIDByName(exchange)
|
||||
if err != nil {
|
||||
log.Error(log.DatabaseMgr, err)
|
||||
log.Errorln(log.DatabaseMgr, err)
|
||||
return nil, err
|
||||
}
|
||||
return getByColumns(append(generateWhereQuery([]string{"exchange_name_id"}, []string{exch.String()}, 0), betweenQuery...))
|
||||
|
||||
@@ -181,7 +181,7 @@ func (m *apiServerManager) StartRESTServer() error {
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&m.restStarted, 0)
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -296,7 +296,7 @@ func (m *apiServerManager) restGetAllEnabledAccountInfo(w http.ResponseWriter, r
|
||||
func (m *apiServerManager) getIndex(w http.ResponseWriter, _ *http.Request) {
|
||||
_, err := fmt.Fprint(w, restIndexResponse)
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -443,7 +443,7 @@ func (m *apiServerManager) StartWebsocketServer() error {
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&m.websocketStarted, 0)
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -502,7 +502,7 @@ func (c *websocketClient) read() {
|
||||
c.Hub.Unregister <- c
|
||||
conErr := c.Conn.Close()
|
||||
if conErr != nil {
|
||||
log.Error(log.APIServerMgr, conErr)
|
||||
log.Errorln(log.APIServerMgr, conErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -547,7 +547,7 @@ func (c *websocketClient) read() {
|
||||
log.Warnf(log.APIServerMgr, "Websocket: request %s failed due to unauthenticated request on an authenticated API\n", evt.Event)
|
||||
err = c.SendWebsocketMessage(WebsocketEventResponse{Event: evt.Event, Error: "unauthorised request on authenticated API"})
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -565,7 +565,7 @@ func (c *websocketClient) write() {
|
||||
defer func() {
|
||||
err := c.Conn.Close()
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
@@ -573,7 +573,7 @@ func (c *websocketClient) write() {
|
||||
if !ok {
|
||||
err := c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
log.Debugln(log.APIServerMgr, "websocket: hub closed the channel")
|
||||
return
|
||||
@@ -586,7 +586,7 @@ func (c *websocketClient) write() {
|
||||
}
|
||||
_, err = w.Write(message)
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
|
||||
// Add queued chat messages to the current websocket message
|
||||
@@ -594,7 +594,7 @@ func (c *websocketClient) write() {
|
||||
for i := 0; i < n; i++ {
|
||||
_, err = w.Write(<-c.Send)
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +661,7 @@ func (m *apiServerManager) WebsocketClientHandler(w http.ResponseWriter, r *http
|
||||
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(log.APIServerMgr, err)
|
||||
log.Errorln(log.APIServerMgr, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -703,7 +703,7 @@ func wsAuth(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -726,7 +726,7 @@ func wsAuth(client *websocketClient, data interface{}) error {
|
||||
client.authFailures++
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
if client.authFailures >= client.maxAuthFailures {
|
||||
log.Debugf(log.APIServerMgr,
|
||||
@@ -765,7 +765,7 @@ func wsSaveConfig(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -776,7 +776,7 @@ func wsSaveConfig(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -786,7 +786,7 @@ func wsSaveConfig(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -826,7 +826,7 @@ func wsGetTicker(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -846,7 +846,7 @@ func wsGetTicker(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -855,7 +855,7 @@ func wsGetTicker(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -886,7 +886,7 @@ func wsGetOrderbook(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -906,7 +906,7 @@ func wsGetOrderbook(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -915,7 +915,7 @@ func wsGetOrderbook(client *websocketClient, data interface{}) error {
|
||||
wsResp.Error = err.Error()
|
||||
sendErr := client.SendWebsocketMessage(wsResp)
|
||||
if sendErr != nil {
|
||||
log.Error(log.APIServerMgr, sendErr)
|
||||
log.Errorln(log.APIServerMgr, sendErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func (m *DatabaseConnectionManager) run(wg *sync.WaitGroup) {
|
||||
case <-t.C:
|
||||
err := m.checkConnection()
|
||||
if err != nil {
|
||||
log.Error(log.DatabaseMgr, "Database connection error:", err)
|
||||
log.Errorln(log.DatabaseMgr, "Database connection error:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (m *DatabaseConnectionManager) checkConnection() error {
|
||||
}
|
||||
|
||||
if !m.dbConn.IsConnected() {
|
||||
log.Info(log.DatabaseMgr, "Database connection reestablished")
|
||||
log.Infoln(log.DatabaseMgr, "Database connection reestablished")
|
||||
m.dbConn.SetConnected(true)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -132,7 +132,7 @@ func (m *DataHistoryManager) retrieveJobs() ([]*DataHistoryJob, error) {
|
||||
}
|
||||
err = m.validateJob(dbJob)
|
||||
if err != nil {
|
||||
log.Error(log.DataHistory, err)
|
||||
log.Errorln(log.DataHistory, err)
|
||||
continue
|
||||
}
|
||||
response = append(response, dbJob)
|
||||
@@ -156,7 +156,7 @@ func (m *DataHistoryManager) PrepareJobs() ([]*DataHistoryJob, error) {
|
||||
defer func() {
|
||||
err = m.Stop()
|
||||
if err != nil {
|
||||
log.Error(log.DataHistory, err)
|
||||
log.Errorln(log.DataHistory, err)
|
||||
}
|
||||
}()
|
||||
return nil, fmt.Errorf("error retrieving jobs, has everything been setup? Data history manager will shut down. %w", err)
|
||||
@@ -237,7 +237,7 @@ func (m *DataHistoryManager) run() {
|
||||
if m.databaseConnectionInstance != nil && m.databaseConnectionInstance.IsConnected() {
|
||||
go func() {
|
||||
if err := m.runJobs(); err != nil {
|
||||
log.Error(log.DataHistory, err)
|
||||
log.Errorln(log.DataHistory, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -274,7 +274,7 @@ func (m *DataHistoryManager) runJobs() error {
|
||||
for i := 0; (i < int(m.maxJobsPerCycle) || m.maxJobsPerCycle == -1) && i < len(validJobs); i++ {
|
||||
err := m.runJob(validJobs[i])
|
||||
if err != nil {
|
||||
log.Error(log.DataHistory, err)
|
||||
log.Errorln(log.DataHistory, err)
|
||||
}
|
||||
if m.verbose {
|
||||
log.Debugf(log.DataHistory, "completed run of data history job %v", validJobs[i].Nickname)
|
||||
|
||||
@@ -90,7 +90,7 @@ func NewFromSettings(settings *Settings, flagSet map[string]bool) (*Engine, erro
|
||||
}
|
||||
|
||||
if *b.Config.Logging.Enabled {
|
||||
err = gctlog.SetupGlobalLogger()
|
||||
err = gctlog.SetupGlobalLogger(b.Config.Name, b.Config.Logging.AdvancedSettings.StructuredLogging)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to setup global logger. %w", err)
|
||||
}
|
||||
@@ -339,7 +339,7 @@ func (bot *Engine) Start() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to set NTP check: %w", err)
|
||||
}
|
||||
gctlog.Info(gctlog.TimeMgr, responseMessage)
|
||||
gctlog.Infoln(gctlog.TimeMgr, responseMessage)
|
||||
}
|
||||
bot.ntpManager, err = setupNTPManager(&bot.Config.NTPClient, *bot.Config.Logging.Enabled)
|
||||
if err != nil {
|
||||
|
||||
@@ -109,7 +109,7 @@ func (m *ntpManager) run() {
|
||||
case <-t.C:
|
||||
err := m.processTime()
|
||||
if err != nil {
|
||||
log.Error(log.TimeMgr, err)
|
||||
log.Errorln(log.TimeMgr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func (m *ntpManager) checkTimeInPools() time.Time {
|
||||
log.Warnf(log.TimeMgr, "Unable to SetDeadline. Error: %s\n", err)
|
||||
err = con.Close()
|
||||
if err != nil {
|
||||
log.Error(log.TimeMgr, err)
|
||||
log.Errorln(log.TimeMgr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func (m *ntpManager) checkTimeInPools() time.Time {
|
||||
log.Warnf(log.TimeMgr, "Unable to write. Error: %s\n", err)
|
||||
err = con.Close()
|
||||
if err != nil {
|
||||
log.Error(log.TimeMgr, err)
|
||||
log.Errorln(log.TimeMgr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (m *ntpManager) checkTimeInPools() time.Time {
|
||||
log.Warnf(log.TimeMgr, "Unable to read. Error: %s\n", err)
|
||||
err = con.Close()
|
||||
if err != nil {
|
||||
log.Error(log.TimeMgr, err)
|
||||
log.Errorln(log.TimeMgr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -196,7 +196,7 @@ func (m *ntpManager) checkTimeInPools() time.Time {
|
||||
|
||||
err = con.Close()
|
||||
if err != nil {
|
||||
log.Error(log.TimeMgr, err)
|
||||
log.Errorln(log.TimeMgr, err)
|
||||
}
|
||||
return time.Unix(int64(secs), nanos)
|
||||
}
|
||||
|
||||
@@ -147,12 +147,12 @@ func (m *OrderManager) CancelAllOrders(ctx context.Context, exchanges []exchange
|
||||
log.Debugf(log.OrderMgr, "Cancelling order(s) for exchange %s.", exchanges[i].GetName())
|
||||
cancel, err := orders[j].DeriveCancel()
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
continue
|
||||
}
|
||||
err = m.Cancel(ctx, cancel)
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,7 +685,7 @@ func (m *OrderManager) processOrders() {
|
||||
var upsertResponse *OrderUpsertResponse
|
||||
upsertResponse, err = m.UpsertOrder(&result[z])
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
continue
|
||||
}
|
||||
for i := range orders {
|
||||
@@ -708,7 +708,7 @@ func (m *OrderManager) processOrders() {
|
||||
var sd time.Time
|
||||
sd, err = m.orderStore.futuresPositionController.LastUpdated()
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
return
|
||||
}
|
||||
if sd.IsZero() {
|
||||
@@ -721,7 +721,7 @@ func (m *OrderManager) processOrders() {
|
||||
})
|
||||
if err != nil {
|
||||
if !errors.Is(err, common.ErrNotYetImplemented) {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -817,7 +817,7 @@ func (m *OrderManager) processMatchingOrders(exch exchange.IBotExchange, orders
|
||||
}
|
||||
err := m.FetchAndUpdateExchangeOrder(exch, &orders[x], orders[x].AssetType)
|
||||
if err != nil {
|
||||
log.Error(log.OrderMgr, err)
|
||||
log.Errorln(log.OrderMgr, err)
|
||||
}
|
||||
}
|
||||
if wg != nil {
|
||||
@@ -919,10 +919,10 @@ func (m *OrderManager) UpsertOrder(od *order.Detail) (resp *OrderUpsertResponse,
|
||||
upsertResponse.OrderDetails.Pair, upsertResponse.OrderDetails.Price, upsertResponse.OrderDetails.Amount,
|
||||
upsertResponse.OrderDetails.Side, upsertResponse.OrderDetails.Type, upsertResponse.OrderDetails.Status)
|
||||
if upsertResponse.IsNewOrder {
|
||||
log.Info(log.OrderMgr, msg)
|
||||
log.Infoln(log.OrderMgr, msg)
|
||||
return upsertResponse, nil
|
||||
}
|
||||
log.Debug(log.OrderMgr, msg)
|
||||
log.Debugln(log.OrderMgr, msg)
|
||||
return upsertResponse, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -709,7 +709,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream
|
||||
defer func() {
|
||||
pipeErr := pipe.Release()
|
||||
if pipeErr != nil {
|
||||
log.Error(log.DispatchMgr, pipeErr)
|
||||
log.Errorln(log.DispatchMgr, pipeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -2183,7 +2183,7 @@ func (s *RPCServer) GetExchangeOrderbookStream(r *gctrpc.GetExchangeOrderbookStr
|
||||
defer func() {
|
||||
pipeErr := pipe.Release()
|
||||
if pipeErr != nil {
|
||||
log.Error(log.DispatchMgr, pipeErr)
|
||||
log.Errorln(log.DispatchMgr, pipeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -2268,7 +2268,7 @@ func (s *RPCServer) GetTickerStream(r *gctrpc.GetTickerStreamRequest, stream gct
|
||||
defer func() {
|
||||
pipeErr := pipe.Release()
|
||||
if pipeErr != nil {
|
||||
log.Error(log.DispatchMgr, pipeErr)
|
||||
log.Errorln(log.DispatchMgr, pipeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -2321,7 +2321,7 @@ func (s *RPCServer) GetExchangeTickerStream(r *gctrpc.GetExchangeTickerStreamReq
|
||||
defer func() {
|
||||
pipeErr := pipe.Release()
|
||||
if pipeErr != nil {
|
||||
log.Error(log.DispatchMgr, pipeErr)
|
||||
log.Errorln(log.DispatchMgr, pipeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -4111,7 +4111,7 @@ func (s *RPCServer) SetDataHistoryJobStatus(_ context.Context, r *gctrpc.SetData
|
||||
status := "success"
|
||||
err := s.dataHistoryManager.SetJobStatus(r.Nickname, r.Id, dataHistoryStatus(r.Status))
|
||||
if err != nil {
|
||||
log.Error(log.GRPCSys, err)
|
||||
log.Errorln(log.GRPCSys, err)
|
||||
status = "failed"
|
||||
}
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ func (m *syncManager) Start() error {
|
||||
log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer stopping.")
|
||||
err := m.Stop()
|
||||
if err != nil {
|
||||
log.Error(log.SyncMgr, err)
|
||||
log.Errorln(log.SyncMgr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -547,7 +547,7 @@ func (m *syncManager) worker() {
|
||||
|
||||
m.add(c)
|
||||
} else {
|
||||
log.Error(log.SyncMgr, err)
|
||||
log.Errorln(log.SyncMgr, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -595,7 +595,7 @@ func (m *syncManager) worker() {
|
||||
}
|
||||
updateErr := m.Update(c.Exchange, c.Pair, c.AssetType, SyncItemOrderbook, err)
|
||||
if updateErr != nil {
|
||||
log.Error(log.SyncMgr, updateErr)
|
||||
log.Errorln(log.SyncMgr, updateErr)
|
||||
}
|
||||
} else {
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
@@ -673,7 +673,7 @@ func (m *syncManager) worker() {
|
||||
}
|
||||
updateErr := m.Update(c.Exchange, c.Pair, c.AssetType, SyncItemTicker, err)
|
||||
if updateErr != nil {
|
||||
log.Error(log.SyncMgr, updateErr)
|
||||
log.Errorln(log.SyncMgr, updateErr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -688,7 +688,7 @@ func (m *syncManager) worker() {
|
||||
m.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, true)
|
||||
err := m.Update(c.Exchange, c.Pair, c.AssetType, SyncItemTrade, nil)
|
||||
if err != nil {
|
||||
log.Error(log.SyncMgr, err)
|
||||
log.Errorln(log.SyncMgr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ func (m *websocketRoutineManager) websocketDataReceiver(ws *stream.Websocket) er
|
||||
for x := range m.dataHandlers {
|
||||
err := m.dataHandlers[x](ws.GetName(), data)
|
||||
if err != nil {
|
||||
log.Error(log.WebsocketMgr, err)
|
||||
log.Errorln(log.WebsocketMgr, err)
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
@@ -190,7 +190,7 @@ func (m *websocketRoutineManager) websocketDataReceiver(ws *stream.Websocket) er
|
||||
func (m *websocketRoutineManager) websocketDataHandler(exchName string, data interface{}) error {
|
||||
switch d := data.(type) {
|
||||
case string:
|
||||
log.Info(log.WebsocketMgr, d)
|
||||
log.Infoln(log.WebsocketMgr, d)
|
||||
case error:
|
||||
return fmt.Errorf("exchange %s websocket error - %s", exchName, data)
|
||||
case stream.FundingData:
|
||||
@@ -267,7 +267,7 @@ func (m *websocketRoutineManager) websocketDataHandler(exchName string, data int
|
||||
case order.ClassificationError:
|
||||
return fmt.Errorf("%w %s", d.Err, d.Error())
|
||||
case stream.UnhandledMessageWarning:
|
||||
log.Warn(log.WebsocketMgr, d.Message)
|
||||
log.Warnln(log.WebsocketMgr, d.Message)
|
||||
case account.Change:
|
||||
if m.verbose {
|
||||
m.printAccountHoldingsChangeSummary(d)
|
||||
|
||||
@@ -21,7 +21,7 @@ func (a *Alphapoint) WebsocketClient() {
|
||||
var httpResp *http.Response
|
||||
endpoint, err := a.API.Endpoints.GetURL(exchange.WebsocketSpot)
|
||||
if err != nil {
|
||||
log.Error(log.WebsocketMgr, err)
|
||||
log.Errorln(log.WebsocketMgr, err)
|
||||
}
|
||||
a.WebsocketConn, httpResp, err = dialer.Dial(endpoint, http.Header{})
|
||||
httpResp.Body.Close() // not used, so safely free the body
|
||||
@@ -38,14 +38,14 @@ func (a *Alphapoint) WebsocketClient() {
|
||||
err = a.WebsocketConn.WriteMessage(websocket.TextMessage, []byte(`{"messageType": "logon"}`))
|
||||
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
return
|
||||
}
|
||||
|
||||
for a.Enabled {
|
||||
msgType, resp, err := a.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func (a *Alphapoint) WebsocketClient() {
|
||||
msgType := MsgType{}
|
||||
err := json.Unmarshal(resp, &msgType)
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (a *Alphapoint) WebsocketClient() {
|
||||
ticker := WebsocketTicker{}
|
||||
err = json.Unmarshal(resp, &ticker)
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ func (b *Binance) batchAggregateTrades(ctx context.Context, arg *AggregatedTrade
|
||||
err := b.SendHTTPRequest(ctx,
|
||||
exchange.RestSpotSupplementary, path, spotDefaultRate, &resp)
|
||||
if err != nil {
|
||||
log.Warn(log.ExchangeSys, err.Error())
|
||||
log.Warnln(log.ExchangeSys, err.Error())
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ func (bi *Binanceus) batchAggregateTrades(ctx context.Context, arg *AggregatedTr
|
||||
err := bi.SendHTTPRequest(ctx,
|
||||
exchange.RestSpotSupplementary, path, spotDefaultRate, &resp)
|
||||
if err != nil {
|
||||
log.Warn(log.ExchangeSys, err.Error())
|
||||
log.Warnln(log.ExchangeSys, err.Error())
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ func (b *Bitmex) Run(ctx context.Context) {
|
||||
if b.Verbose {
|
||||
wsEndpoint, err := b.API.Endpoints.GetURL(exchange.WebsocketSpot)
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%s Websocket: %s. (url: %s).\n",
|
||||
|
||||
@@ -60,7 +60,7 @@ func (c *COINUT) WsConnect() error {
|
||||
err = c.wsAuthenticate(context.TODO())
|
||||
if err != nil {
|
||||
c.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
log.Error(log.WebsocketMgr, err)
|
||||
log.Errorln(log.WebsocketMgr, err)
|
||||
}
|
||||
|
||||
// define bi-directional communication
|
||||
|
||||
@@ -150,7 +150,7 @@ func (g *Gemini) Setup(exch *config.Exchange) error {
|
||||
if exch.UseSandbox {
|
||||
err = g.API.Endpoints.SetRunning(exchange.RestSpot.String(), geminiSandboxAPIURL)
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error {
|
||||
}
|
||||
err := h.Websocket.AuthConn.SendJSONMessage(authPing)
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -446,7 +446,7 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error {
|
||||
func (h *HUOBI) sendPingResponse(pong int64) {
|
||||
err := h.Websocket.Conn.SendJSONMessage(WsPong{Pong: pong})
|
||||
if err != nil {
|
||||
log.Error(log.ExchangeSys, err)
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ func (o *OKCoin) WsHandleData(respRaw []byte) error {
|
||||
}
|
||||
}
|
||||
if o.Verbose {
|
||||
log.Debug(log.ExchangeSys,
|
||||
log.Debugln(log.ExchangeSys,
|
||||
o.Name+" - "+eventResponse.Event+" on channel: "+eventResponse.Channel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ func (w *Websocket) connectionMonitor() error {
|
||||
if w.IsConnected() {
|
||||
err := w.Shutdown()
|
||||
if err != nil {
|
||||
log.Error(log.WebsocketMgr, err)
|
||||
log.Errorln(log.WebsocketMgr, err)
|
||||
}
|
||||
}
|
||||
if w.verbose {
|
||||
@@ -402,7 +402,7 @@ func (w *Websocket) connectionMonitor() error {
|
||||
if !w.IsConnecting() && !w.IsConnected() {
|
||||
err := w.Connect()
|
||||
if err != nil {
|
||||
log.Error(log.WebsocketMgr, err)
|
||||
log.Errorln(log.WebsocketMgr, err)
|
||||
}
|
||||
}
|
||||
if !timer.Stop() {
|
||||
|
||||
@@ -114,7 +114,7 @@ func AddTradesToBuffer(exchangeName string, data ...Data) error {
|
||||
func (p *Processor) Run(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
if !atomic.CompareAndSwapInt32(&p.started, 0, 1) {
|
||||
log.Error(log.Trade, "trade processor already started")
|
||||
log.Errorln(log.Trade, "trade processor already started")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
@@ -136,7 +136,7 @@ func (p *Processor) Run(wg *sync.WaitGroup) {
|
||||
}
|
||||
err := SaveTradesToDatabase(bufferCopy...)
|
||||
if err != nil {
|
||||
log.Error(log.Trade, err)
|
||||
log.Errorln(log.Trade, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// NewVM attempts to create a new Virtual Machine firstly from pool
|
||||
func (g *GctScriptManager) NewVM() *VM {
|
||||
if !g.IsRunning() {
|
||||
log.Error(log.GCTScriptMgr, Error{
|
||||
log.Errorln(log.GCTScriptMgr, Error{
|
||||
Action: "NewVM",
|
||||
Cause: ErrScriptingDisabled,
|
||||
})
|
||||
@@ -34,7 +34,7 @@ func (g *GctScriptManager) NewVM() *VM {
|
||||
}
|
||||
newUUID, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, Error{Action: "New: UUID", Cause: err})
|
||||
log.Errorln(log.GCTScriptMgr, Error{Action: "New: UUID", Cause: err})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (g *GctScriptManager) NewVM() *VM {
|
||||
|
||||
s, ok := pool.Get().(*tengo.Script)
|
||||
if !ok {
|
||||
log.Error(log.GCTScriptMgr, Error{
|
||||
log.Errorln(log.GCTScriptMgr, Error{
|
||||
Action: "NewVM",
|
||||
Cause: errors.New("unable to type assert tengo script"),
|
||||
})
|
||||
@@ -144,30 +144,30 @@ func (vm *VM) CompileAndRun() {
|
||||
}
|
||||
err := vm.Compile()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
err = vm.unregister()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = vm.RunCtx()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
err = vm.unregister()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if vm.Compiled.Get("timer").String() != "" {
|
||||
vm.T, err = time.ParseDuration(vm.Compiled.Get("timer").String())
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
err = vm.Shutdown()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -177,12 +177,12 @@ func (vm *VM) CompileAndRun() {
|
||||
}
|
||||
|
||||
if vm.T < 0 {
|
||||
log.Error(log.GCTScriptMgr, "Repeat timer cannot be under 1 nano second")
|
||||
log.Errorln(log.GCTScriptMgr, "Repeat timer cannot be under 1 nano second")
|
||||
}
|
||||
}
|
||||
err = vm.Shutdown()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func (vm *VM) runner() {
|
||||
vm.NextRun = time.Now().Add(vm.T)
|
||||
err := vm.RunCtx()
|
||||
if err != nil {
|
||||
log.Error(log.GCTScriptMgr, err)
|
||||
log.Errorln(log.GCTScriptMgr, err)
|
||||
return
|
||||
}
|
||||
case <-vm.S:
|
||||
|
||||
@@ -13,7 +13,7 @@ var (
|
||||
ErrSubLoggerAlreadyRegistered = errors.New("sub logger already registered")
|
||||
)
|
||||
|
||||
func newLogger(c *Config) Logger {
|
||||
func newLogger(c *Config, botName string) Logger {
|
||||
return Logger{
|
||||
TimestampFormat: c.AdvancedSettings.TimeStampFormat,
|
||||
Spacer: c.AdvancedSettings.Spacer,
|
||||
@@ -23,6 +23,7 @@ func newLogger(c *Config) Logger {
|
||||
DebugHeader: c.AdvancedSettings.Headers.Debug,
|
||||
ShowLogSystemName: c.AdvancedSettings.ShowLogSystemName != nil && *c.AdvancedSettings.ShowLogSystemName,
|
||||
BypassJobChannelFilledWarning: c.AdvancedSettings.BypassJobChannelFilledWarning,
|
||||
botName: botName,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -9,9 +10,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errWriterAlreadyLoaded = errors.New("io.Writer already loaded")
|
||||
errJobsChannelIsFull = errors.New("logger jobs channel is filled")
|
||||
errWriterIsNil = errors.New("io writer is nil")
|
||||
errWriterAlreadyLoaded = errors.New("io.Writer already loaded")
|
||||
errJobsChannelIsFull = errors.New("logger jobs channel is filled")
|
||||
errWriterIsNil = errors.New("io writer is nil")
|
||||
message Key = "message"
|
||||
timestamp Key = "timestamp"
|
||||
severity Key = "severity"
|
||||
subLoggerName Key = "sublogger"
|
||||
botName Key = "botname"
|
||||
)
|
||||
|
||||
// loggerWorker handles all work staged to be written to configured io.Writer(s)
|
||||
@@ -22,28 +28,58 @@ func loggerWorker() {
|
||||
buffer := make([]byte, 0, defaultBufferCapacity)
|
||||
var n int
|
||||
var err error
|
||||
|
||||
structuredOutbound := ExtraFields{}
|
||||
for j := range jobsChannel {
|
||||
if j.Passback != nil {
|
||||
j.Passback <- struct{}{}
|
||||
continue
|
||||
}
|
||||
data := j.fn()
|
||||
buffer = append(buffer, j.Header...)
|
||||
if j.ShowLogSystemName {
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
buffer = append(buffer, j.SlName...)
|
||||
}
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
if j.TimestampFormat != "" {
|
||||
buffer = time.Now().AppendFormat(buffer, j.TimestampFormat)
|
||||
}
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
buffer = append(buffer, data...)
|
||||
if data == "" || data[len(data)-1] != '\n' {
|
||||
msg := j.fn()
|
||||
if j.StructuredLogging {
|
||||
structuredOutbound[message] = msg
|
||||
structuredOutbound[timestamp] = time.Now().UnixMilli()
|
||||
structuredOutbound[severity] = j.Severity
|
||||
structuredOutbound[subLoggerName] = j.SubLoggerName
|
||||
structuredOutbound[botName] = j.Instance
|
||||
for k, v := range j.StructuredFields {
|
||||
_, ok := structuredOutbound[k]
|
||||
if ok {
|
||||
// Disallow overwriting of key values
|
||||
displayError(fmt.Errorf("structured logging: cannot overwrite key [%s]", k))
|
||||
continue
|
||||
}
|
||||
structuredOutbound[k] = v
|
||||
}
|
||||
buffer, err = json.Marshal(structuredOutbound)
|
||||
if err != nil {
|
||||
log.Println("log: failed to marshal structured log data:", err)
|
||||
}
|
||||
for k := range j.StructuredFields {
|
||||
// Delete non-persistent structured fields
|
||||
delete(structuredOutbound, k)
|
||||
}
|
||||
buffer = append(buffer, '\n')
|
||||
} else {
|
||||
buffer = append(buffer, j.Header...)
|
||||
if j.ShowLogSystemName {
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
buffer = append(buffer, []byte(j.SubLoggerName)...)
|
||||
}
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
if j.TimestampFormat != "" {
|
||||
buffer = time.Now().AppendFormat(buffer, j.TimestampFormat)
|
||||
}
|
||||
buffer = append(buffer, j.Spacer...)
|
||||
buffer = append(buffer, msg...)
|
||||
if msg == "" || msg[len(msg)-1] != '\n' {
|
||||
buffer = append(buffer, '\n')
|
||||
}
|
||||
}
|
||||
|
||||
for x := range j.Writers {
|
||||
// NOTE: byte slice is not copied, this is a pointer to the buffer.
|
||||
// This is only safe if the buffer is not modified after this point.
|
||||
n, err = j.Writers[x].Write(buffer)
|
||||
if err != nil {
|
||||
displayError(fmt.Errorf("%T %w", j.Writers[x], err))
|
||||
@@ -51,7 +87,7 @@ func loggerWorker() {
|
||||
displayError(fmt.Errorf("%T %w", j.Writers[x], io.ErrShortWrite))
|
||||
}
|
||||
}
|
||||
buffer = buffer[:0] // Clean buffer
|
||||
buffer = buffer[:0] // Clean buffer for next use
|
||||
jobsPool.Put(j)
|
||||
}
|
||||
}
|
||||
@@ -63,15 +99,19 @@ type deferral func() string
|
||||
// StageLogEvent stages a new logger event in a jobs channel to be processed by
|
||||
// a worker pool. This segregates the need to process the log string and the
|
||||
// writes to the required io.Writer.
|
||||
func (mw *multiWriterHolder) StageLogEvent(fn deferral, header, slName, spacer, timestampFormat string, showLogSystemName, bypassWarning bool) {
|
||||
func (mw *multiWriterHolder) StageLogEvent(fn deferral, header, slName, spacer, timestampFormat, instance, level string, showLogSystemName, bypassWarning, structuredLog bool, fields map[Key]interface{}) {
|
||||
newJob := jobsPool.Get().(*job) //nolint:forcetypeassert // Not necessary from a pool
|
||||
newJob.Writers = mw.writers
|
||||
newJob.fn = fn
|
||||
newJob.Header = header
|
||||
newJob.SlName = slName
|
||||
newJob.SubLoggerName = slName
|
||||
newJob.ShowLogSystemName = showLogSystemName
|
||||
newJob.Spacer = spacer
|
||||
newJob.TimestampFormat = timestampFormat
|
||||
newJob.Instance = instance
|
||||
newJob.StructuredFields = fields
|
||||
newJob.StructuredLogging = structuredLog
|
||||
newJob.Severity = level
|
||||
|
||||
select {
|
||||
case jobsChannel <- newJob:
|
||||
|
||||
@@ -147,8 +147,9 @@ func SetupSubLoggers(s []SubLoggerConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupGlobalLogger setup the global loggers with the default global config values
|
||||
func SetupGlobalLogger() error {
|
||||
// SetupGlobalLogger setup the global loggers with the default global config
|
||||
// values.
|
||||
func SetupGlobalLogger(botName string, structuredOutput bool) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
@@ -167,12 +168,14 @@ func SetupGlobalLogger() error {
|
||||
|
||||
for _, subLogger := range SubLoggers {
|
||||
subLogger.setLevels(splitLevel(globalLogConfig.Level))
|
||||
subLogger.structuredLogging = structuredOutput
|
||||
err = subLogger.setOutput(writers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
subLogger.botName = botName
|
||||
}
|
||||
logger = newLogger(globalLogConfig)
|
||||
logger = newLogger(globalLogConfig, botName)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -213,9 +216,11 @@ func registerNewSubLogger(subLogger string) *SubLogger {
|
||||
}
|
||||
|
||||
temp := &SubLogger{
|
||||
name: strings.ToUpper(subLogger),
|
||||
output: tempHolder,
|
||||
levels: splitLevel("INFO|WARN|DEBUG|ERROR"),
|
||||
name: strings.ToUpper(subLogger),
|
||||
output: tempHolder,
|
||||
levels: splitLevel("INFO|WARN|DEBUG|ERROR"),
|
||||
botName: logger.botName,
|
||||
structuredLogging: globalLogConfig != nil && globalLogConfig.AdvancedSettings.StructuredLogging,
|
||||
}
|
||||
SubLoggers[subLogger] = temp
|
||||
return temp
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
)
|
||||
|
||||
@@ -76,7 +78,7 @@ func setupTestLoggers() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetupGlobalLogger()
|
||||
err = SetupGlobalLogger("test", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -88,7 +90,7 @@ func SetupDisabled() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = SetupGlobalLogger()
|
||||
err = SetupGlobalLogger("test", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,17 +197,17 @@ var errWriteError = errors.New("write error")
|
||||
func TestMultiWriterWrite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fields := &logFields{}
|
||||
f := &fields{}
|
||||
buff := newTestBuffer()
|
||||
|
||||
var err error
|
||||
fields.output, err = multiWriter(io.Discard, buff)
|
||||
f.output, err = multiWriter(io.Discard, buff)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
payload := "woooooooooooooooooooooooooooooooooooow"
|
||||
fields.output.StageLogEvent(func() string { return payload }, "", "", "", "", false, false)
|
||||
f.output.StageLogEvent(func() string { return payload }, "", "", "", "", "", "", false, false, false, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -215,17 +217,17 @@ func TestMultiWriterWrite(t *testing.T) {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, payload)
|
||||
}
|
||||
|
||||
fields.output, err = multiWriter(&WriteShorter{}, io.Discard)
|
||||
f.output, err = multiWriter(&WriteShorter{}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fields.output.StageLogEvent(func() string { return payload }, "", "", "", "", false, false) // Will display error: Logger write error: *log.WriteShorter short write
|
||||
f.output.StageLogEvent(func() string { return payload }, "", "", "", "", "", "", false, false, false, nil) // Will display error: Logger write error: *log.WriteShorter short write
|
||||
|
||||
fields.output, err = multiWriter(&WriteError{}, io.Discard)
|
||||
f.output, err = multiWriter(&WriteError{}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fields.output.StageLogEvent(func() string { return payload }, "", "", "", "", false, false) // Will display error: Logger write error: *log.WriteError write error
|
||||
f.output.StageLogEvent(func() string { return payload }, "", "", "", "", "", "", false, false, false, nil) // Will display error: Logger write error: *log.WriteError write error
|
||||
}
|
||||
|
||||
func TestGetWriters(t *testing.T) {
|
||||
@@ -343,8 +345,8 @@ func TestStageNewLogEvent(t *testing.T) {
|
||||
w := newTestBuffer()
|
||||
mw := &multiWriterHolder{writers: []io.Writer{w}}
|
||||
|
||||
fields := &logFields{output: mw}
|
||||
fields.output.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", " space ", "", false, false)
|
||||
f := &fields{output: mw}
|
||||
f.output.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", " space ", "", "", "", false, false, false, nil)
|
||||
|
||||
<-w.Finished
|
||||
if contents := w.Read(); contents != "header space space out\n" { //nolint:dupword // False positive
|
||||
@@ -354,6 +356,7 @@ func TestStageNewLogEvent(t *testing.T) {
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := newTestBuffer()
|
||||
mw := &multiWriterHolder{writers: []io.Writer{w}}
|
||||
|
||||
@@ -367,26 +370,22 @@ func TestInfo(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Info(sl, "Hello")
|
||||
<-w.Finished
|
||||
contents := w.Read()
|
||||
|
||||
if !strings.Contains(contents, "Hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "Hello")
|
||||
}
|
||||
Infof(nil, "%s", "bad")
|
||||
|
||||
Infof(sl, "%s", "hello")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
contents := w.Read()
|
||||
if !strings.Contains(contents, "hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello")
|
||||
}
|
||||
|
||||
Infoln(nil, "hello", "bad")
|
||||
|
||||
Infoln(sl, "hello", "goodbye")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
if !strings.Contains(contents, "hello goodbye") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello goodbye")
|
||||
if !strings.Contains(contents, "hellogoodbye") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hellogoodbye")
|
||||
}
|
||||
|
||||
_, err = SetLevel("TESTYMCTESTALOTINFO", "")
|
||||
@@ -396,12 +395,6 @@ func TestInfo(t *testing.T) {
|
||||
|
||||
// Should not write to buffer at all as it should return if functionality
|
||||
// is not enabled.
|
||||
Info(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "")
|
||||
}
|
||||
|
||||
Infoln(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
@@ -424,26 +417,22 @@ func TestDebug(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Debug(sl, "Hello")
|
||||
<-w.Finished
|
||||
contents := w.Read()
|
||||
|
||||
if !strings.Contains(contents, "Hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "Hello")
|
||||
}
|
||||
Debugf(nil, "%s", "bad")
|
||||
|
||||
Debugf(sl, "%s", "hello")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
contents := w.Read()
|
||||
if !strings.Contains(contents, "hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello")
|
||||
}
|
||||
|
||||
Debugln(nil, ":sun_with_face:", "bad")
|
||||
|
||||
Debugln(sl, ":sun_with_face:", ":angrysun:")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
if !strings.Contains(contents, ":sun_with_face: :angrysun:") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, ":sun_with_face: :angrysun:")
|
||||
if !strings.Contains(contents, ":sun_with_face::angrysun:") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, ":sun_with_face::angrysun:")
|
||||
}
|
||||
|
||||
_, err = SetLevel("TESTYMCTESTALOTDEBUG", "")
|
||||
@@ -453,12 +442,6 @@ func TestDebug(t *testing.T) {
|
||||
|
||||
// Should not write to buffer at all as it should return if functionality
|
||||
// is not enabled.
|
||||
Debug(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "")
|
||||
}
|
||||
|
||||
Debugln(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
@@ -481,26 +464,22 @@ func TestWarn(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Warn(sl, "Hello")
|
||||
<-w.Finished
|
||||
contents := w.Read()
|
||||
|
||||
if !strings.Contains(contents, "Hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "Hello")
|
||||
}
|
||||
Warnf(nil, "%s", "silly")
|
||||
|
||||
Warnf(sl, "%s", "hello")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
contents := w.Read()
|
||||
if !strings.Contains(contents, "hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello")
|
||||
}
|
||||
|
||||
Warnln(nil, "super", "silly")
|
||||
|
||||
Warnln(sl, "hello", "world")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
if !strings.Contains(contents, "hello world") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello world")
|
||||
if !strings.Contains(contents, "helloworld") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "helloworld")
|
||||
}
|
||||
|
||||
_, err = SetLevel("TESTYMCTESTALOTWARN", "")
|
||||
@@ -510,12 +489,6 @@ func TestWarn(t *testing.T) {
|
||||
|
||||
// Should not write to buffer at all as it shhould return if functionality
|
||||
// is not enabled.
|
||||
Warn(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "")
|
||||
}
|
||||
|
||||
Warnln(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
@@ -543,26 +516,22 @@ func TestError(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Error(sl, "Hello")
|
||||
<-w.Finished
|
||||
contents := w.Read()
|
||||
|
||||
if !strings.Contains(contents, "Hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "Hello")
|
||||
}
|
||||
Errorf(nil, "%s", "oh wow")
|
||||
|
||||
Errorf(sl, "%s", "hello")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
contents := w.Read()
|
||||
if !strings.Contains(contents, "hello") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello")
|
||||
}
|
||||
|
||||
Errorln(nil, "nil", "days")
|
||||
|
||||
Errorln(sl, "hello", "goodbye")
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
if !strings.Contains(contents, "hello goodbye") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hello goodbye")
|
||||
if !strings.Contains(contents, "hellogoodbye") {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "hellogoodbye")
|
||||
}
|
||||
|
||||
_, err = SetLevel("TESTYMCTESTALOTERROR", "")
|
||||
@@ -572,12 +541,6 @@ func TestError(t *testing.T) {
|
||||
|
||||
// Should not write to buffer at all as it shhould return if functionality
|
||||
// is not enabled.
|
||||
Error(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", contents, "")
|
||||
}
|
||||
|
||||
Errorln(sl, "HelloHello")
|
||||
contents = w.Read()
|
||||
if contents != "" {
|
||||
@@ -602,14 +565,14 @@ func TestSubLoggerName(t *testing.T) {
|
||||
w := newTestBuffer()
|
||||
mw := &multiWriterHolder{writers: []io.Writer{w}}
|
||||
|
||||
mw.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", "||", time.RFC3339, true, false)
|
||||
mw.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", "||", "", "", time.RFC3339, true, false, false, nil)
|
||||
<-w.Finished
|
||||
contents := w.Read()
|
||||
if !strings.Contains(contents, "SUBLOGGER") {
|
||||
t.Error("Expected SUBLOGGER in output")
|
||||
}
|
||||
|
||||
mw.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", "||", time.RFC3339, false, false)
|
||||
mw.StageLogEvent(func() string { return "out" }, "header", "SUBLOGGER", "||", "", "", time.RFC3339, false, false, false, nil)
|
||||
<-w.Finished
|
||||
contents = w.Read()
|
||||
if strings.Contains(contents, "SUBLOGGER") {
|
||||
@@ -629,7 +592,7 @@ func TestNewSubLogger(t *testing.T) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
|
||||
Debug(sl, "testerinos")
|
||||
Debugln(sl, "testerinos")
|
||||
|
||||
_, err = NewSubLogger("TESTERINOS")
|
||||
if !errors.Is(err, ErrSubLoggerAlreadyRegistered) {
|
||||
@@ -694,19 +657,28 @@ func TestOpenNew(t *testing.T) {
|
||||
}
|
||||
|
||||
type testBuffer struct {
|
||||
value string
|
||||
value []byte
|
||||
Finished chan struct{}
|
||||
}
|
||||
|
||||
func (tb *testBuffer) Write(p []byte) (int, error) {
|
||||
tb.value = string(p)
|
||||
cpy := make([]byte, len(p))
|
||||
copy(cpy, p)
|
||||
tb.value = cpy
|
||||
tb.Finished <- struct{}{}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (tb *testBuffer) Read() string {
|
||||
defer func() { tb.value = "" }()
|
||||
return tb.value
|
||||
defer func() { tb.value = tb.value[:0] }()
|
||||
return string(tb.value)
|
||||
}
|
||||
|
||||
func (tb *testBuffer) ReadRaw() []byte {
|
||||
defer func() { tb.value = tb.value[:0] }()
|
||||
cpy := make([]byte, len(tb.value))
|
||||
copy(cpy, tb.value)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func newTestBuffer() *testBuffer {
|
||||
@@ -717,7 +689,7 @@ func newTestBuffer() *testBuffer {
|
||||
func BenchmarkNewLogEvent(b *testing.B) {
|
||||
mw := &multiWriterHolder{writers: []io.Writer{io.Discard}}
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.StageLogEvent(func() string { return "somedata" }, "header", "sublog", "||", time.RFC3339, true, false)
|
||||
mw.StageLogEvent(func() string { return "somedata" }, "header", "sublog", "||", "", "", time.RFC3339, true, false, false, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,7 +697,7 @@ func BenchmarkNewLogEvent(b *testing.B) {
|
||||
func BenchmarkInfo(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Info(Global, "Hello this is an info benchmark")
|
||||
Infoln(Global, "Hello this is an info benchmark")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +709,7 @@ func BenchmarkInfoDisabled(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
Info(Global, "Hello this is an info benchmark")
|
||||
Infoln(Global, "Hello this is an info benchmark")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,3 +728,144 @@ func BenchmarkInfoln(b *testing.B) {
|
||||
Infoln(Global, "Hello this is an infoln benchmark")
|
||||
}
|
||||
}
|
||||
|
||||
type testCapture struct {
|
||||
Message string `json:"message"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Severity string `json:"severity"`
|
||||
SubLogger string `json:"sublogger"`
|
||||
BotName string `json:"botname"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
}
|
||||
|
||||
func TestWithFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
writer := newTestBuffer()
|
||||
mwh := &multiWriterHolder{writers: []io.Writer{writer}}
|
||||
|
||||
sl, err := NewSubLogger("TESTSTRUCTUREDLOGGING")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sl.structuredLogging = true
|
||||
sl.setLevelsProtected(splitLevel("DEBUG|ERROR|INFO|WARN"))
|
||||
err = sl.setOutputProtected(mwh)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
id, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ErrorlnWithFields(nil, ExtraFields{"id": id}, "nilerinos")
|
||||
ErrorlnWithFields(sl, ExtraFields{"id": id}, "hello")
|
||||
<-writer.Finished
|
||||
var captured testCapture
|
||||
bro := writer.ReadRaw()
|
||||
err = json.Unmarshal(bro, &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err, string(bro))
|
||||
}
|
||||
checkCapture(t, &captured, id, "hello", "error")
|
||||
|
||||
ErrorfWithFields(nil, ExtraFields{"id": id}, "%v", "nilerinos")
|
||||
ErrorfWithFields(sl, ExtraFields{"id": id}, "%v", "good")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "good", "error")
|
||||
|
||||
DebuglnWithFields(nil, ExtraFields{"id": id}, "nilerinos")
|
||||
DebuglnWithFields(sl, ExtraFields{"id": id}, "sir")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "sir", "debug")
|
||||
|
||||
DebugfWithFields(nil, ExtraFields{"id": id}, "%v", "nilerinos")
|
||||
DebugfWithFields(sl, ExtraFields{"id": id}, "%v", "how")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "how", "debug")
|
||||
|
||||
WarnlnWithFields(nil, ExtraFields{"id": id}, "nilerinos")
|
||||
WarnlnWithFields(sl, ExtraFields{"id": id}, "are")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "are", "warn")
|
||||
|
||||
WarnfWithFields(nil, ExtraFields{"id": id}, "%v", "nilerinos")
|
||||
WarnfWithFields(sl, ExtraFields{"id": id}, "%v", "you")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "you", "warn")
|
||||
|
||||
InfolnWithFields(nil, ExtraFields{"id": id}, "nilerinos")
|
||||
InfolnWithFields(sl, ExtraFields{"id": id}, "today")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "today", "info")
|
||||
|
||||
InfofWithFields(nil, ExtraFields{"id": id}, "%v", "nilerinos")
|
||||
InfofWithFields(sl, ExtraFields{"id": id}, "%v", "?")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCapture(t, &captured, id, "?", "info")
|
||||
|
||||
// Conflicting fields
|
||||
InfofWithFields(nil, ExtraFields{botName: "lol"}, "%v", "nilerinos")
|
||||
InfofWithFields(sl, ExtraFields{botName: "lol"}, "%v", "?")
|
||||
<-writer.Finished
|
||||
err = json.Unmarshal(writer.ReadRaw(), &captured)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if captured.BotName != "test" {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", captured.BotName, "test")
|
||||
}
|
||||
}
|
||||
|
||||
func checkCapture(t *testing.T, c *testCapture, expID uuid.UUID, expMessage, expSeverity string) {
|
||||
t.Helper()
|
||||
|
||||
if c.ID != expID {
|
||||
t.Errorf("received: '%v' but expected: '%v'", c.ID, expID)
|
||||
}
|
||||
|
||||
if c.Message != expMessage {
|
||||
t.Errorf("received: '%v' but expected: '%v'", c.Message, expMessage)
|
||||
}
|
||||
|
||||
if c.Severity != expSeverity {
|
||||
t.Errorf("received: '%v' but expected: '%v'", c.Severity, expSeverity)
|
||||
}
|
||||
|
||||
if c.SubLogger != "TESTSTRUCTUREDLOGGING" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", c.SubLogger, "TESTSTRUCTUREDLOGGING")
|
||||
}
|
||||
|
||||
if c.BotName != "test" {
|
||||
t.Errorf("received: '%v' but expected: '%v'", c.BotName, "test")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ var (
|
||||
|
||||
// Note: Logger state within logFields will be persistent until it's garbage
|
||||
// collected. This is a little bit more efficient.
|
||||
logFieldsPool = &sync.Pool{New: func() interface{} { return &logFields{logger: logger} }}
|
||||
logFieldsPool = &sync.Pool{New: func() interface{} { return &fields{logger: logger} }}
|
||||
|
||||
// LogPath system path to store log files in
|
||||
logPath string
|
||||
@@ -44,10 +44,14 @@ type job struct {
|
||||
Writers []io.Writer
|
||||
fn deferral
|
||||
Header string
|
||||
SlName string
|
||||
SubLoggerName string
|
||||
Spacer string
|
||||
TimestampFormat string
|
||||
ShowLogSystemName bool
|
||||
Instance string
|
||||
StructuredFields map[Key]interface{}
|
||||
StructuredLogging bool
|
||||
Severity string
|
||||
Passback chan<- struct{}
|
||||
}
|
||||
|
||||
@@ -66,6 +70,7 @@ type advancedSettings struct {
|
||||
TimeStampFormat string `json:"timeStampFormat"`
|
||||
Headers headers `json:"headers"`
|
||||
BypassJobChannelFilledWarning bool `json:"bypassJobChannelFilledWarning"`
|
||||
StructuredLogging bool `json:"structuredLogging"`
|
||||
}
|
||||
|
||||
type headers struct {
|
||||
@@ -95,6 +100,8 @@ type Logger struct {
|
||||
TimestampFormat string
|
||||
InfoHeader, ErrorHeader, DebugHeader, WarnHeader string
|
||||
Spacer string
|
||||
Level string
|
||||
botName string
|
||||
}
|
||||
|
||||
// Levels flags for each sub logger type
|
||||
@@ -105,3 +112,11 @@ type Levels struct {
|
||||
type multiWriterHolder struct {
|
||||
writers []io.Writer
|
||||
}
|
||||
|
||||
// ExtraFields is a map of key value pairs that can be added to a structured
|
||||
// log output.
|
||||
type ExtraFields map[Key]interface{}
|
||||
|
||||
// Key is used for structured logging fields to ensure no collisions occur.
|
||||
// Unexported keys are default fields which cannot be overwritten.
|
||||
type Key string
|
||||
|
||||
369
log/loggers.go
369
log/loggers.go
@@ -5,244 +5,213 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// Info takes a pointer subLogger struct and string sends to StageLogEvent
|
||||
func Info(sl *SubLogger, data string) {
|
||||
// Infoln is a logging function that takes a sublogger and an arbitrary number
|
||||
// of interface{} arguments. This writes to configured io.Writer(s) as an
|
||||
// information message using default formats for its operands. A new line is
|
||||
// automatically added to the output.
|
||||
func Infoln(sl *SubLogger, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.stageln(f.logger.InfoHeader, a...)
|
||||
}
|
||||
if fields.info {
|
||||
fields.output.StageLogEvent(func() string { return data },
|
||||
fields.logger.InfoHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Infoln takes a pointer subLogger struct and interface sends to StageLogEvent
|
||||
func Infoln(sl *SubLogger, v ...interface{}) {
|
||||
// InfolnWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields and an arbitrary number of interface{} arguments.
|
||||
// This writes to configured io.Writer(s) as an information message using
|
||||
// default formats for its operands. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func InfolnWithFields(sl *SubLogger, extra ExtraFields, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stageln(f.logger.InfoHeader, a...)
|
||||
}
|
||||
if fields.info {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintln(v...) },
|
||||
fields.logger.InfoHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Infof takes a pointer subLogger struct, string and interface formats sends to StageLogEvent
|
||||
func Infof(sl *SubLogger, data string, v ...interface{}) {
|
||||
// Infof is a logging function that takes a sublogger, a format string along
|
||||
// with optional arguments. This writes to configured io.Writer(s) as an
|
||||
// information message which formats according to the format specifier.
|
||||
// A new line is automatically added to the output.
|
||||
func Infof(sl *SubLogger, format string, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
sl.getFields().stagef(f.logger.InfoHeader, format, a...)
|
||||
}
|
||||
if fields.info {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintf(data, v...) },
|
||||
fields.logger.InfoHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Debug takes a pointer subLogger struct and string sends to StageLogEvent
|
||||
func Debug(sl *SubLogger, data string) {
|
||||
// InfofWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields, a format string along with optional arguments.
|
||||
// This writes to configured io.Writer(s) as an information message which
|
||||
// formats according to the format specifier. A new line is automatically added
|
||||
// to the output. If structured logging is not enabled, the fields will be
|
||||
// ignored.
|
||||
func InfofWithFields(sl *SubLogger, extra ExtraFields, format string, a ...interface{}) { //nolint:goprintffuncname // False positive
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stagef(f.logger.InfoHeader, format, a...)
|
||||
}
|
||||
if fields.debug {
|
||||
fields.output.StageLogEvent(func() string { return data },
|
||||
fields.logger.DebugHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Debugln takes a pointer subLogger struct, string and interface sends to StageLogEvent
|
||||
// Debugln is a logging function that takes a sublogger and an arbitrary number
|
||||
// of interface{} arguments. This writes to configured io.Writer(s) as an
|
||||
// debug message using default formats for its operands. A new line is
|
||||
// automatically added to the output.
|
||||
func Debugln(sl *SubLogger, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.stageln(f.logger.DebugHeader, v...)
|
||||
}
|
||||
if fields.debug {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintln(v...) },
|
||||
fields.logger.DebugHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Debugf takes a pointer subLogger struct, string and interface formats sends to StageLogEvent
|
||||
// DebuglnWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields and an arbitrary number of interface{} arguments.
|
||||
// This writes to configured io.Writer(s) as an debug message using default
|
||||
// formats for its operands. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func DebuglnWithFields(sl *SubLogger, extra ExtraFields, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stageln(f.logger.DebugHeader, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf is a logging function that takes a sublogger, a format string along
|
||||
// with optional arguments. This writes to configured io.Writer(s) as an
|
||||
// debug message which formats according to the format specifier. A new line is
|
||||
// automatically added to the output.
|
||||
func Debugf(sl *SubLogger, data string, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
sl.getFields().stagef(f.logger.DebugHeader, data, v...)
|
||||
}
|
||||
if fields.debug {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintf(data, v...) },
|
||||
fields.logger.DebugHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Warn takes a pointer subLogger struct & string and sends to StageLogEvent
|
||||
func Warn(sl *SubLogger, data string) {
|
||||
// DebugfWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields, a format string along with optional arguments.
|
||||
// This writes to configured io.Writer(s) as an debug message which formats
|
||||
// according to the format specifier. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func DebugfWithFields(sl *SubLogger, extra ExtraFields, format string, a ...interface{}) { //nolint:goprintffuncname // False positive
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stagef(f.logger.DebugHeader, format, a...)
|
||||
}
|
||||
if fields.warn {
|
||||
fields.output.StageLogEvent(func() string { return data },
|
||||
fields.logger.WarnHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Warnln takes a pointer subLogger struct & interface formats and sends to StageLogEvent
|
||||
// Warnln is a logging function that takes a sublogger and an arbitrary number
|
||||
// of interface{} arguments. This writes to configured io.Writer(s) as an
|
||||
// warning message using default formats for its operands. A new line is
|
||||
// automatically added to the output.
|
||||
func Warnln(sl *SubLogger, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.stageln(f.logger.WarnHeader, v...)
|
||||
}
|
||||
if fields.warn {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintln(v...) },
|
||||
fields.logger.WarnHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Warnf takes a pointer subLogger struct, string and interface formats sends to StageLogEvent
|
||||
// WarnlnWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields and an arbitrary number of interface{} arguments.
|
||||
// This writes to configured io.Writer(s) as an warning message using default
|
||||
// formats for its operands. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func WarnlnWithFields(sl *SubLogger, extra ExtraFields, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stageln(f.logger.WarnHeader, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf is a logging function that takes a sublogger, a format string along
|
||||
// with optional arguments. This writes to configured io.Writer(s) as an
|
||||
// warning message which formats according to the format specifier. A new line
|
||||
// is automatically added to the output.
|
||||
func Warnf(sl *SubLogger, data string, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
sl.getFields().stagef(f.logger.WarnHeader, data, v...)
|
||||
}
|
||||
if fields.warn {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintf(data, v...) },
|
||||
fields.logger.WarnHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Error takes a pointer subLogger struct & interface formats and sends to StageLogEvent
|
||||
func Error(sl *SubLogger, data ...interface{}) {
|
||||
// WarnfWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields, a format string along with optional arguments.
|
||||
// This writes to configured io.Writer(s) as an warning message which formats
|
||||
// according to the format specifier. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func WarnfWithFields(sl *SubLogger, extra ExtraFields, format string, a ...interface{}) { //nolint:goprintffuncname // False positive
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stagef(f.logger.WarnHeader, format, a...)
|
||||
}
|
||||
if fields.error {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprint(data...) },
|
||||
fields.logger.ErrorHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Errorln takes a pointer subLogger struct, string & interface formats and sends to StageLogEvent
|
||||
// Errorln is a logging function that takes a sublogger and an arbitrary number
|
||||
// of interface{} arguments. This writes to configured io.Writer(s) as an
|
||||
// error message using default formats for its operands. A new line is
|
||||
// automatically added to the output.
|
||||
func Errorln(sl *SubLogger, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.stageln(f.logger.ErrorHeader, v...)
|
||||
}
|
||||
if fields.error {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintln(v...) },
|
||||
fields.logger.ErrorHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
// Errorf takes a pointer subLogger struct, string and interface formats sends to StageLogEvent
|
||||
// ErrorlnWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields and an arbitrary number of interface{} arguments.
|
||||
// This writes to configured io.Writer(s) as an error message using default
|
||||
// formats for its operands. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func ErrorlnWithFields(sl *SubLogger, extra ExtraFields, a ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stageln(f.logger.ErrorHeader, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf is a logging function that takes a sublogger, a format string along
|
||||
// with optional arguments. This writes to configured io.Writer(s) as an
|
||||
// error message which formats according to the format specifier. A new line
|
||||
// is automatically added to the output.
|
||||
func Errorf(sl *SubLogger, data string, v ...interface{}) {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
fields := sl.getFields()
|
||||
if fields == nil {
|
||||
return
|
||||
if f := sl.getFields(); f != nil {
|
||||
sl.getFields().stagef(f.logger.ErrorHeader, data, v...)
|
||||
}
|
||||
if fields.error {
|
||||
fields.output.StageLogEvent(func() string { return fmt.Sprintf(data, v...) },
|
||||
fields.logger.ErrorHeader,
|
||||
fields.name,
|
||||
fields.logger.Spacer,
|
||||
fields.logger.TimestampFormat,
|
||||
fields.logger.ShowLogSystemName,
|
||||
fields.logger.BypassJobChannelFilledWarning)
|
||||
}
|
||||
|
||||
// ErrorfWithFields is a logging function that takes a sublogger, additional
|
||||
// structured logging fields, a format string along with optional arguments.
|
||||
// This writes to configured io.Writer(s) as an error message which formats
|
||||
// according to the format specifier. A new line is automatically added to the
|
||||
// output. If structured logging is not enabled, the fields will be ignored.
|
||||
func ErrorfWithFields(sl *SubLogger, extra ExtraFields, format string, a ...interface{}) { //nolint:goprintffuncname // False positive
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if f := sl.getFields(); f != nil {
|
||||
f.structuredFields = extra
|
||||
f.stagef(f.logger.ErrorHeader, format, a...)
|
||||
}
|
||||
logFieldsPool.Put(fields)
|
||||
}
|
||||
|
||||
func displayError(err error) {
|
||||
@@ -250,3 +219,57 @@ func displayError(err error) {
|
||||
log.Printf("Logger write error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// enabled checks if the log level is enabled
|
||||
func (l *fields) enabled(header string) string {
|
||||
switch header {
|
||||
case l.logger.InfoHeader:
|
||||
if l.info {
|
||||
return "info"
|
||||
}
|
||||
case l.logger.WarnHeader:
|
||||
if l.warn {
|
||||
return "warn"
|
||||
}
|
||||
case l.logger.ErrorHeader:
|
||||
if l.error {
|
||||
return "error"
|
||||
}
|
||||
case l.logger.DebugHeader:
|
||||
if l.debug {
|
||||
return "debug"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// stage stages a log event
|
||||
func (l *fields) stage(header string, deferFunc deferral) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
if level := l.enabled(header); level != "" {
|
||||
l.output.StageLogEvent(deferFunc,
|
||||
header,
|
||||
l.name,
|
||||
l.logger.Spacer,
|
||||
l.logger.TimestampFormat,
|
||||
l.botName,
|
||||
level,
|
||||
l.logger.ShowLogSystemName,
|
||||
l.logger.BypassJobChannelFilledWarning,
|
||||
l.structuredLogging,
|
||||
l.structuredFields)
|
||||
}
|
||||
logFieldsPool.Put(l)
|
||||
}
|
||||
|
||||
// stageln stages a log event
|
||||
func (l *fields) stageln(header string, a ...interface{}) {
|
||||
l.stage(header, func() string { return fmt.Sprint(a...) })
|
||||
}
|
||||
|
||||
// stagef stages a log event
|
||||
func (l *fields) stagef(header, format string, a ...interface{}) {
|
||||
l.stage(header, func() string { return fmt.Sprintf(format, a...) })
|
||||
}
|
||||
|
||||
@@ -38,17 +38,18 @@ func (sl *SubLogger) setLevels(newLevels Levels) {
|
||||
|
||||
// getFields returns sub logger specific fields for the potential log job.
|
||||
// Note: Calling function must have mutex lock in place.
|
||||
func (sl *SubLogger) getFields() *logFields {
|
||||
func (sl *SubLogger) getFields() *fields {
|
||||
if sl == nil || globalLogConfig == nil || globalLogConfig.Enabled == nil || !*globalLogConfig.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := logFieldsPool.Get().(*logFields) //nolint:forcetypeassert // Not necessary from a pool
|
||||
fields.info = sl.levels.Info
|
||||
fields.warn = sl.levels.Warn
|
||||
fields.debug = sl.levels.Debug
|
||||
fields.error = sl.levels.Error
|
||||
fields.name = sl.name
|
||||
fields.output = sl.output
|
||||
return fields
|
||||
f := logFieldsPool.Get().(*fields) //nolint:forcetypeassert // Not necessary from a pool
|
||||
f.info = sl.levels.Info
|
||||
f.warn = sl.levels.Warn
|
||||
f.debug = sl.levels.Debug
|
||||
f.error = sl.levels.Error
|
||||
f.name = sl.name
|
||||
f.output = sl.output
|
||||
f.botName = sl.botName
|
||||
f.structuredLogging = sl.structuredLogging
|
||||
return f
|
||||
}
|
||||
|
||||
@@ -35,19 +35,24 @@ var (
|
||||
// SubLogger defines a sub logger can be used externally for packages wanted to
|
||||
// leverage GCT library logger features.
|
||||
type SubLogger struct {
|
||||
name string
|
||||
levels Levels
|
||||
output *multiWriterHolder
|
||||
name string
|
||||
levels Levels
|
||||
output *multiWriterHolder
|
||||
botName string
|
||||
structuredLogging bool
|
||||
}
|
||||
|
||||
// logFields is used to store data in a non-global and thread-safe manner
|
||||
// fields is used to store data in a non-global and thread-safe manner
|
||||
// so logs cannot be modified mid-log causing a data-race issue
|
||||
type logFields struct {
|
||||
info bool
|
||||
warn bool
|
||||
debug bool
|
||||
error bool
|
||||
name string
|
||||
output *multiWriterHolder
|
||||
logger Logger
|
||||
type fields struct {
|
||||
info bool
|
||||
warn bool
|
||||
debug bool
|
||||
error bool
|
||||
structuredLogging bool
|
||||
name string
|
||||
output *multiWriterHolder
|
||||
logger Logger
|
||||
botName string
|
||||
structuredFields ExtraFields
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user