github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/logs/log.go (about) 1 // the package is exported from github.com/beego/beego/v2/core/logs 2 3 // Copyright 2023. All Rights Reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 // Package logs provide a general log interface 18 // Usage: 19 // 20 // import "github.com/mdaxf/iac/framework/logs" 21 // 22 // log := NewLogger(10000) 23 // log.SetLogger("console", "") 24 // 25 // > the first params stand for how many channel 26 // 27 // Use it like this: 28 // 29 // log.Trace("trace") 30 // log.Info("info") 31 // log.Warn("warning") 32 // log.Debug("debug") 33 // log.Critical("critical") 34 package logs 35 36 import ( 37 "fmt" 38 "log" 39 "os" 40 "runtime" 41 "strings" 42 "sync" 43 "time" 44 ) 45 46 // RFC5424 log message levels. 47 const ( 48 LevelEmergency = iota 49 LevelAlert 50 LevelCritical 51 LevelError 52 LevelWarning 53 LevelNotice 54 LevelInformational 55 LevelDebug 56 LeverPerformance 57 ) 58 59 // levelLogLogger is defined to implement log.Logger 60 // the real log level will be LevelEmergency 61 const levelLoggerImpl = -1 62 63 // Name for adapter with iac official support 64 const ( 65 AdapterConsole = "console" 66 AdapterFile = "file" 67 AdapterMultiFile = "multifile" 68 AdapterMail = "smtp" 69 AdapterConn = "conn" 70 AdapterEs = "es" 71 AdapterSlack = "slack" 72 AdapterAliLS = "alils" 73 AdapterDocumentDB = "documentdb" 74 ) 75 76 // Legacy log level constants to ensure backwards compatibility. 77 const ( 78 LevelInfo = LevelInformational 79 LevelTrace = LevelDebug 80 LevelWarn = LevelWarning 81 ) 82 83 type newLoggerFunc func() Logger 84 85 // Logger defines the behavior of a log provider. 86 type Logger interface { 87 Init(config string) error 88 WriteMsg(lm *LogMsg) error 89 Destroy() 90 Flush() 91 SetFormatter(f LogFormatter) 92 } 93 94 var ( 95 adapters = make(map[string]newLoggerFunc) 96 levelPrefix = [LeverPerformance + 1]string{"[M]", "[A]", "[C]", "[E]", "[W]", "[N]", "[I]", "[D]", "[P]"} 97 ) 98 99 // Register makes a log provide available by the provided name. 100 // If Register is called twice with the same name or if driver is nil, 101 // it panics. 102 func Register(name string, log newLoggerFunc) { 103 if log == nil { 104 panic("logs: Register provide is nil") 105 } 106 if _, dup := adapters[name]; dup { 107 panic("logs: Register called twice for provider " + name) 108 } 109 adapters[name] = log 110 } 111 112 // IACLogger is default logger in iac application. 113 // Can contain several providers and log message into all providers. 114 type IACLogger struct { 115 lock sync.Mutex 116 init bool 117 enableFuncCallDepth bool 118 enableFullFilePath bool 119 asynchronous bool 120 // Whether to discard logs when buffer is full and asynchronous is true 121 // No discard by default 122 logWithNonBlocking bool 123 wg sync.WaitGroup 124 level int 125 loggerFuncCallDepth int 126 prefix string 127 msgChanLen int64 128 msgChan chan *LogMsg 129 closeChan chan struct{} 130 flushChan chan struct{} 131 outputs []*nameLogger 132 globalFormatter string 133 Perf bool 134 Threhold int 135 } 136 137 const defaultAsyncMsgLen = 1e3 138 139 type nameLogger struct { 140 Logger 141 name string 142 } 143 144 const moduleName = "logger" 145 146 var logMsgPool *sync.Pool 147 148 // NewLogger returns a new IACLogger. 149 // channelLen: the number of messages in chan(used where asynchronous is true). 150 // if the buffering chan is full, logger adapters write to file or other way. 151 // func NewLogger(channelLens ...int64) *IACLogger { 152 func NewLogger(channelLens ...int64) *IACLogger { 153 bl := new(IACLogger) 154 bl.level = LevelDebug 155 bl.loggerFuncCallDepth = 3 156 bl.msgChanLen = append(channelLens, 0)[0] 157 if bl.msgChanLen <= 0 { 158 bl.msgChanLen = defaultAsyncMsgLen 159 } 160 bl.Perf = true 161 bl.flushChan = make(chan struct{}, 1) 162 bl.closeChan = make(chan struct{}, 1) 163 bl.Threhold = 10 164 // bl.setLogger(AdapterConsole) 165 return bl 166 } 167 168 func New() *IACLogger { 169 bl := &IACLogger{} 170 bl.level = LevelDebug 171 bl.loggerFuncCallDepth = 3 172 bl.msgChanLen = 0 173 if bl.msgChanLen <= 0 { 174 bl.msgChanLen = defaultAsyncMsgLen 175 } 176 bl.Perf = true 177 bl.flushChan = make(chan struct{}, 1) 178 bl.closeChan = make(chan struct{}, 1) 179 bl.Threhold = 10 180 bl.setLogger(AdapterConsole) 181 return bl 182 } 183 184 // Async sets the log to asynchronous and start the goroutine 185 func (bl *IACLogger) Async(msgLen ...int64) *IACLogger { 186 bl.lock.Lock() 187 defer bl.lock.Unlock() 188 if bl.asynchronous { 189 return bl 190 } 191 bl.asynchronous = true 192 if len(msgLen) > 0 && msgLen[0] > 0 { 193 bl.msgChanLen = msgLen[0] 194 } 195 bl.msgChan = make(chan *LogMsg, bl.msgChanLen) 196 logMsgPool = &sync.Pool{ 197 New: func() interface{} { 198 return &LogMsg{} 199 }, 200 } 201 bl.wg.Add(1) 202 go bl.startLogger() 203 return bl 204 } 205 206 // AsyncNonBlockWrite Non-blocking write in asynchronous mode 207 // Only works if asynchronous write logging is set 208 func (bl *IACLogger) AsyncNonBlockWrite() *IACLogger { 209 if !bl.asynchronous { 210 return bl 211 } 212 bl.logWithNonBlocking = true 213 return bl 214 } 215 216 // SetLogger provides a given logger adapter into IACLogger with config string. 217 // config must in in JSON format like {"interval":360}} 218 func (bl *IACLogger) setLogger(adapterName string, configs ...string) error { 219 config := append(configs, "{}")[0] 220 // fmt.Println("log config:", config) 221 for _, l := range bl.outputs { 222 if l.name == adapterName { 223 return fmt.Errorf("logs: duplicate adaptername %q (you have set this logger before)", adapterName) 224 } 225 } 226 // fmt.Println("adapterName:", adapterName) 227 logAdapter, ok := adapters[adapterName] 228 // fmt.Println("logAdapter:", logAdapter) 229 if !ok { 230 return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName) 231 } 232 233 lg := logAdapter() 234 // fmt.Println("lg:", lg) 235 err := lg.Init(config) 236 // fmt.Println("err:", err) 237 if err != nil { 238 return err 239 } 240 241 // Global formatter overrides the default set formatter 242 if len(bl.globalFormatter) > 0 { 243 fmtr, ok := GetFormatter(bl.globalFormatter) 244 if !ok { 245 return fmt.Errorf("the formatter with name: %s not found", bl.globalFormatter) 246 } 247 lg.SetFormatter(fmtr) 248 } 249 250 bl.outputs = append(bl.outputs, &nameLogger{name: adapterName, Logger: lg}) 251 return nil 252 } 253 254 // SetLogger provides a given logger adapter into IACLogger with config string. 255 // config must in in JSON format like {"interval":360}} 256 func (bl *IACLogger) SetLogger(adapterName string, configs ...string) error { 257 bl.lock.Lock() 258 defer bl.lock.Unlock() 259 if !bl.init { 260 bl.Perf = true 261 bl.Threhold = 10 262 bl.outputs = []*nameLogger{} 263 bl.init = true 264 } 265 return bl.setLogger(adapterName, configs...) 266 } 267 268 // DelLogger removes a logger adapter in IACLogger. 269 func (bl *IACLogger) DelLogger(adapterName string) error { 270 bl.lock.Lock() 271 defer bl.lock.Unlock() 272 outputs := make([]*nameLogger, 0, len(bl.outputs)) 273 for _, lg := range bl.outputs { 274 if lg.name == adapterName { 275 lg.Destroy() 276 } else { 277 outputs = append(outputs, lg) 278 } 279 } 280 if len(outputs) == len(bl.outputs) { 281 return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adapterName) 282 } 283 bl.outputs = outputs 284 return nil 285 } 286 287 func (bl *IACLogger) writeToLoggers(lm *LogMsg) { 288 for _, l := range bl.outputs { 289 err := l.WriteMsg(lm) 290 if err != nil { 291 fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) 292 } 293 } 294 } 295 296 func (bl *IACLogger) Write(p []byte) (n int, err error) { 297 if len(p) == 0 { 298 return 0, nil 299 } 300 // writeMsg will always add a '\n' character 301 if p[len(p)-1] == '\n' { 302 p = p[0 : len(p)-1] 303 } 304 lm := &LogMsg{ 305 Msg: string(p), 306 Level: levelLoggerImpl, 307 When: time.Now(), 308 } 309 310 // set levelLoggerImpl to ensure all log message will be write out 311 err = bl.writeMsg(lm) 312 if err == nil { 313 return len(p), nil 314 } 315 return 0, err 316 } 317 318 func (bl *IACLogger) writeMsg(lm *LogMsg) error { 319 if !bl.init { 320 bl.lock.Lock() 321 bl.setLogger(AdapterConsole) 322 bl.lock.Unlock() 323 bl.Perf = true 324 bl.Threhold = 10 325 } 326 327 var ( 328 file string 329 line int 330 ok bool 331 ) 332 333 _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) 334 if !ok { 335 file = "???" 336 line = 0 337 } 338 lm.FilePath = file 339 lm.LineNumber = line 340 lm.Prefix = bl.prefix 341 342 lm.enableFullFilePath = bl.enableFullFilePath 343 lm.enableFuncCallDepth = bl.enableFuncCallDepth 344 345 // set level info in front of filename info 346 if lm.Level == levelLoggerImpl { 347 // set to emergency to ensure all log will be print out correctly 348 lm.Level = LevelEmergency 349 } 350 351 if bl.asynchronous { 352 logM := logMsgPool.Get().(*LogMsg) 353 logM.Level = lm.Level 354 logM.Msg = lm.Msg 355 logM.When = lm.When 356 logM.Args = lm.Args 357 logM.FilePath = lm.FilePath 358 logM.LineNumber = lm.LineNumber 359 logM.Prefix = lm.Prefix 360 361 if bl.outputs != nil { 362 if bl.logWithNonBlocking { 363 select { 364 case bl.msgChan <- lm: 365 // discard log when channel is full 366 default: 367 } 368 } else { 369 bl.msgChan <- lm 370 } 371 } else { 372 logMsgPool.Put(lm) 373 } 374 } else { 375 bl.writeToLoggers(lm) 376 } 377 return nil 378 } 379 380 // SetLevel sets log message level. 381 // If message level (such as LevelDebug) is higher than logger level (such as LevelWarning), 382 // log providers will not be sent the message. 383 func (bl *IACLogger) SetLevel(l int) { 384 bl.level = l 385 } 386 387 // GetLevel Get Current log message level. 388 func (bl *IACLogger) GetLevel() int { 389 return bl.level 390 } 391 392 // SetLogFuncCallDepth set log funcCallDepth 393 func (bl *IACLogger) SetLogFuncCallDepth(d int) { 394 bl.loggerFuncCallDepth = d 395 } 396 397 // GetLogFuncCallDepth return log funcCallDepth for wrapper 398 func (bl *IACLogger) GetLogFuncCallDepth() int { 399 return bl.loggerFuncCallDepth 400 } 401 402 // EnableFuncCallDepth enable log funcCallDepth 403 func (bl *IACLogger) EnableFuncCallDepth(b bool) { 404 bl.enableFuncCallDepth = b 405 } 406 407 // set prefix 408 func (bl *IACLogger) SetPrefix(s string) { 409 bl.prefix = s 410 } 411 412 // start logger chan reading. 413 // when chan is not empty, write logs. 414 func (bl *IACLogger) startLogger() { 415 gameOver := false 416 for { 417 select { 418 case bm, ok := <-bl.msgChan: 419 // this is a terrible design to have a signal channel that accept two inputs 420 // so we only handle the msg if the channel is not closed 421 if ok { 422 bl.writeToLoggers(bm) 423 logMsgPool.Put(bm) 424 } 425 case <-bl.closeChan: 426 bl.flush() 427 for _, l := range bl.outputs { 428 l.Destroy() 429 } 430 bl.outputs = nil 431 gameOver = true 432 bl.wg.Done() 433 case <-bl.flushChan: 434 bl.flush() 435 bl.wg.Done() 436 } 437 if gameOver { 438 break 439 } 440 } 441 } 442 443 func (bl *IACLogger) setGlobalFormatter(fmtter string) error { 444 bl.globalFormatter = fmtter 445 return nil 446 } 447 448 // SetGlobalFormatter sets the global formatter for all log adapters 449 // don't forget to register the formatter by invoking RegisterFormatter 450 func SetGlobalFormatter(fmtter string) error { 451 return iacLogger.setGlobalFormatter(fmtter) 452 } 453 454 // Emergency Log EMERGENCY level message. 455 func (bl *IACLogger) Emergency(format string, v ...interface{}) { 456 if LevelEmergency > bl.level { 457 return 458 } 459 460 lm := &LogMsg{ 461 Level: LevelEmergency, 462 Msg: format, 463 When: time.Now(), 464 } 465 if len(v) > 0 { 466 lm.Msg = fmt.Sprintf(lm.Msg, v...) 467 } 468 469 bl.writeMsg(lm) 470 } 471 472 // Alert Log ALERT level message. 473 func (bl *IACLogger) Alert(format string, v ...interface{}) { 474 if LevelAlert > bl.level { 475 return 476 } 477 478 lm := &LogMsg{ 479 Level: LevelAlert, 480 Msg: format, 481 When: time.Now(), 482 Args: v, 483 } 484 bl.writeMsg(lm) 485 } 486 487 // Critical Log CRITICAL level message. 488 func (bl *IACLogger) Critical(format string, v ...interface{}) { 489 if LevelCritical > bl.level { 490 return 491 } 492 lm := &LogMsg{ 493 Level: LevelCritical, 494 Msg: format, 495 When: time.Now(), 496 Args: v, 497 } 498 499 bl.writeMsg(lm) 500 } 501 502 // Error Log ERROR level message. 503 func (bl *IACLogger) Error(format string, v ...interface{}) { 504 if LevelError > bl.level { 505 return 506 } 507 lm := &LogMsg{ 508 Level: LevelError, 509 Msg: format, 510 When: time.Now(), 511 Args: v, 512 } 513 514 bl.writeMsg(lm) 515 } 516 517 // Warning Log WARNING level message. 518 func (bl *IACLogger) Warning(format string, v ...interface{}) { 519 if LevelWarn > bl.level { 520 return 521 } 522 lm := &LogMsg{ 523 Level: LevelWarn, 524 Msg: format, 525 When: time.Now(), 526 Args: v, 527 } 528 529 bl.writeMsg(lm) 530 } 531 532 // Notice Log NOTICE level message. 533 func (bl *IACLogger) Notice(format string, v ...interface{}) { 534 if LevelNotice > bl.level { 535 return 536 } 537 lm := &LogMsg{ 538 Level: LevelNotice, 539 Msg: format, 540 When: time.Now(), 541 Args: v, 542 } 543 544 bl.writeMsg(lm) 545 } 546 547 // Informational Log INFORMATIONAL level message. 548 func (bl *IACLogger) Informational(format string, v ...interface{}) { 549 if LevelInfo > bl.level { 550 return 551 } 552 lm := &LogMsg{ 553 Level: LevelInfo, 554 Msg: format, 555 When: time.Now(), 556 Args: v, 557 } 558 559 bl.writeMsg(lm) 560 } 561 562 // Debug Log DEBUG level message. 563 func (bl *IACLogger) Debug(format string, v ...interface{}) { 564 if LevelDebug > bl.level { 565 return 566 } 567 lm := &LogMsg{ 568 Level: LevelDebug, 569 Msg: format, 570 When: time.Now(), 571 Args: v, 572 } 573 574 bl.writeMsg(lm) 575 } 576 577 // Warn Log WARN level message. 578 // compatibility alias for Warning() 579 func (bl *IACLogger) Warn(format string, v ...interface{}) { 580 if LevelWarn > bl.level { 581 return 582 } 583 lm := &LogMsg{ 584 Level: LevelWarn, 585 Msg: format, 586 When: time.Now(), 587 Args: v, 588 } 589 590 bl.writeMsg(lm) 591 } 592 593 // Info Log INFO level message. 594 // compatibility alias for Informational() 595 func (bl *IACLogger) Info(format string, v ...interface{}) { 596 if LevelInfo > bl.level { 597 return 598 } 599 lm := &LogMsg{ 600 Level: LevelInfo, 601 Msg: format, 602 When: time.Now(), 603 Args: v, 604 } 605 606 bl.writeMsg(lm) 607 } 608 609 // Performance level message./ 610 func (bl *IACLogger) Performance(elapsed time.Duration, format string, v ...interface{}) { 611 612 // fmt.Println(bl, bl.Perf, bl.Threhold, elapsed.Milliseconds(), format, v) 613 614 if bl == nil { 615 return 616 } 617 if bl.Perf { 618 619 if elsapsedTime := elapsed.Milliseconds(); elsapsedTime > int64(bl.Threhold) { 620 621 lm := &LogMsg{ 622 Level: LeverPerformance, 623 Msg: format, 624 When: time.Now(), 625 Args: v, 626 } 627 628 bl.writeMsg(lm) 629 630 // lm.Level = LevelDebug 631 // bl.writeMsg(lm) 632 } 633 } 634 } 635 636 // Trace Log TRACE level message. 637 // compatibility alias for Debug() 638 func (bl *IACLogger) Trace(format string, v ...interface{}) { 639 if LevelDebug > bl.level { 640 return 641 } 642 lm := &LogMsg{ 643 Level: LevelDebug, 644 Msg: format, 645 When: time.Now(), 646 Args: v, 647 } 648 649 bl.writeMsg(lm) 650 } 651 652 // Flush flush all chan data. 653 func (bl *IACLogger) Flush() { 654 if bl.asynchronous { 655 bl.flushChan <- struct{}{} 656 bl.wg.Wait() 657 bl.wg.Add(1) 658 return 659 } 660 bl.flush() 661 } 662 663 // Close close logger, flush all chan data and destroy all adapters in IACLogger. 664 func (bl *IACLogger) Close() { 665 if bl.asynchronous { 666 bl.closeChan <- struct{}{} 667 bl.wg.Wait() 668 close(bl.msgChan) 669 } else { 670 bl.flush() 671 for _, l := range bl.outputs { 672 l.Destroy() 673 } 674 bl.outputs = nil 675 } 676 close(bl.flushChan) 677 close(bl.closeChan) 678 } 679 680 // Reset close all outputs, and set bl.outputs to nil 681 func (bl *IACLogger) Reset() { 682 bl.Flush() 683 for _, l := range bl.outputs { 684 l.Destroy() 685 } 686 bl.outputs = nil 687 } 688 689 func (bl *IACLogger) flush() { 690 if bl.asynchronous { 691 for { 692 if len(bl.msgChan) > 0 { 693 bm, ok := <-bl.msgChan 694 if !ok { 695 continue 696 } 697 bl.writeToLoggers(bm) 698 logMsgPool.Put(bm) 699 continue 700 } 701 break 702 } 703 } 704 for _, l := range bl.outputs { 705 l.Flush() 706 } 707 } 708 709 // iacLogger references the used application logger. 710 var iacLogger = NewLogger() 711 712 // GetIACLogger returns the default IACLogger 713 func GetIACLogger() *IACLogger { 714 return iacLogger 715 } 716 717 var iacLoggerMap = struct { 718 sync.RWMutex 719 logs map[string]*log.Logger 720 }{ 721 logs: map[string]*log.Logger{}, 722 } 723 724 // GetLogger returns the default IACLogger 725 func GetLogger(prefixes ...string) *log.Logger { 726 prefix := append(prefixes, "")[0] 727 if prefix != "" { 728 prefix = fmt.Sprintf(`[%s] `, strings.ToUpper(prefix)) 729 } 730 iacLoggerMap.RLock() 731 l, ok := iacLoggerMap.logs[prefix] 732 if ok { 733 iacLoggerMap.RUnlock() 734 return l 735 } 736 iacLoggerMap.RUnlock() 737 iacLoggerMap.Lock() 738 defer iacLoggerMap.Unlock() 739 l, ok = iacLoggerMap.logs[prefix] 740 if !ok { 741 l = log.New(iacLogger, prefix, 0) 742 iacLoggerMap.logs[prefix] = l 743 } 744 return l 745 } 746 747 // EnableFullFilePath enables full file path logging. Disabled by default 748 // e.g "/home/Documents/GitHub/iac/mainapp/" instead of "mainapp" 749 func EnableFullFilePath(b bool) { 750 iacLogger.enableFullFilePath = b 751 } 752 753 // Reset will remove all the adapter 754 func Reset() { 755 iacLogger.Reset() 756 } 757 758 // Async set the beelogger with Async mode and hold msglen messages 759 func Async(msgLen ...int64) *IACLogger { 760 return iacLogger.Async(msgLen...) 761 } 762 763 // SetLevel sets the global log level used by the simple logger. 764 func SetLevel(l int) { 765 iacLogger.SetLevel(l) 766 } 767 768 // SetPrefix sets the prefix 769 func SetPrefix(s string) { 770 iacLogger.SetPrefix(s) 771 } 772 773 // EnableFuncCallDepth enable log funcCallDepth 774 func EnableFuncCallDepth(b bool) { 775 iacLogger.enableFuncCallDepth = b 776 } 777 778 // SetLogFuncCall set the CallDepth, default is 4 779 func SetLogFuncCall(b bool) { 780 iacLogger.EnableFuncCallDepth(b) 781 iacLogger.SetLogFuncCallDepth(3) 782 } 783 784 // SetLogFuncCallDepth set log funcCallDepth 785 func SetLogFuncCallDepth(d int) { 786 iacLogger.loggerFuncCallDepth = d 787 } 788 789 // SetLogger sets a new logger. 790 func SetLogger(adapter string, config ...string) error { 791 return iacLogger.SetLogger(adapter, config...) 792 } 793 794 // Emergency logs a message at emergency level. 795 func Emergency(f interface{}, v ...interface{}) { 796 iacLogger.Emergency(formatPattern(f, v...), v...) 797 } 798 799 // Alert logs a message at alert level. 800 func Alert(f interface{}, v ...interface{}) { 801 iacLogger.Alert(formatPattern(f, v...), v...) 802 } 803 804 // Critical logs a message at critical level. 805 func Critical(f interface{}, v ...interface{}) { 806 iacLogger.Critical(formatPattern(f, v...), v...) 807 } 808 809 // Error logs a message at error level. 810 func Error(f interface{}, v ...interface{}) { 811 iacLogger.Error(formatPattern(f, v...), v...) 812 } 813 814 // Warning logs a message at warning level. 815 func Warning(f interface{}, v ...interface{}) { 816 iacLogger.Warn(formatPattern(f, v...), v...) 817 } 818 819 // Warn compatibility alias for Warning() 820 func Warn(f interface{}, v ...interface{}) { 821 iacLogger.Warn(formatPattern(f, v...), v...) 822 } 823 824 // Notice logs a message at notice level. 825 func Notice(f interface{}, v ...interface{}) { 826 iacLogger.Notice(formatPattern(f, v...), v...) 827 } 828 829 // Informational logs a message at info level. 830 func Informational(f interface{}, v ...interface{}) { 831 iacLogger.Info(formatPattern(f, v...), v...) 832 } 833 834 // Info compatibility alias for Warning() 835 func Info(f interface{}, v ...interface{}) { 836 iacLogger.Info(formatPattern(f, v...), v...) 837 } 838 839 // Debug logs a message at debug level. 840 func Debug(f interface{}, v ...interface{}) { 841 iacLogger.Debug(formatPattern(f, v...), v...) 842 } 843 844 // Trace logs a message at trace level. 845 // compatibility alias for Warning() 846 func Trace(f interface{}, v ...interface{}) { 847 iacLogger.Trace(formatPattern(f, v...), v...) 848 } 849 850 func formatPattern(f interface{}, v ...interface{}) string { 851 var msg string 852 switch f.(type) { 853 case string: 854 msg = f.(string) 855 if len(v) == 0 { 856 return msg 857 } 858 if !strings.Contains(msg, "%") { 859 // do not contain format char 860 msg += strings.Repeat(" %v", len(v)) 861 } 862 default: 863 msg = fmt.Sprint(f) 864 if len(v) == 0 { 865 return msg 866 } 867 msg += strings.Repeat(" %v", len(v)) 868 } 869 return msg 870 }