log.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * Copyright (c) 2014-2015, Yawning Angel <yawning at schwanenlied dot me>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. *
  11. * * Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  19. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. * POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. // Package log implements a simple set of leveled logging wrappers around the
  28. // standard log package.
  29. package log // import "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird/common/log"
  30. import (
  31. "fmt"
  32. "io/ioutil"
  33. "log"
  34. "net"
  35. "os"
  36. "strings"
  37. )
  38. const (
  39. elidedAddr = "[scrubbed]"
  40. // LevelError is the ERROR log level (NOTICE/ERROR).
  41. LevelError = iota
  42. // LevelWarn is the WARN log level, (NOTICE/ERROR/WARN).
  43. LevelWarn
  44. // LevelInfo is the INFO log level, (NOTICE/ERROR/WARN/INFO).
  45. LevelInfo
  46. // LevelDebug is the DEBUG log level, (NOTICE/ERROR/WARN/INFO/DEBUG).
  47. LevelDebug
  48. )
  49. var logLevel = LevelInfo
  50. var enableLogging bool
  51. var unsafeLogging bool
  52. // Init initializes logging with the given path, and log safety options.
  53. func Init(enable bool, logFilePath string, unsafe bool) error {
  54. if enable {
  55. f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
  56. if err != nil {
  57. return err
  58. }
  59. log.SetOutput(f)
  60. } else {
  61. log.SetOutput(ioutil.Discard)
  62. }
  63. enableLogging = enable
  64. unsafeLogging = unsafe
  65. return nil
  66. }
  67. // Enabled returns if logging is enabled.
  68. func Enabled() bool {
  69. return enableLogging
  70. }
  71. // Unsafe returns if unsafe logging is allowed (the caller MAY skip eliding
  72. // addresses and other bits of sensitive information).
  73. func Unsafe() bool {
  74. return unsafeLogging
  75. }
  76. // Level returns the current log level.
  77. func Level() int {
  78. return logLevel
  79. }
  80. // SetLogLevel sets the log level to the value indicated by the given string
  81. // (case-insensitive).
  82. func SetLogLevel(logLevelStr string) error {
  83. switch strings.ToUpper(logLevelStr) {
  84. case "ERROR":
  85. logLevel = LevelError
  86. case "WARN":
  87. logLevel = LevelWarn
  88. case "INFO":
  89. logLevel = LevelInfo
  90. case "DEBUG":
  91. logLevel = LevelDebug
  92. default:
  93. return fmt.Errorf("invalid log level '%s'", logLevelStr)
  94. }
  95. return nil
  96. }
  97. // Noticef logs the given format string/arguments at the NOTICE log level.
  98. // Unless logging is disabled, Noticef logs are always emitted.
  99. func Noticef(format string, a ...interface{}) {
  100. if enableLogging {
  101. msg := fmt.Sprintf(format, a...)
  102. log.Print("[NOTICE]: " + msg)
  103. }
  104. }
  105. // Errorf logs the given format string/arguments at the ERROR log level.
  106. func Errorf(format string, a ...interface{}) {
  107. if enableLogging && logLevel >= LevelError {
  108. msg := fmt.Sprintf(format, a...)
  109. log.Print("[ERROR]: " + msg)
  110. }
  111. }
  112. // Warnf logs the given format string/arguments at the WARN log level.
  113. func Warnf(format string, a ...interface{}) {
  114. if enableLogging && logLevel >= LevelWarn {
  115. msg := fmt.Sprintf(format, a...)
  116. log.Print("[WARN]: " + msg)
  117. }
  118. }
  119. // Infof logs the given format string/arguments at the INFO log level.
  120. func Infof(format string, a ...interface{}) {
  121. if enableLogging && logLevel >= LevelInfo {
  122. msg := fmt.Sprintf(format, a...)
  123. log.Print("[INFO]: " + msg)
  124. }
  125. }
  126. // Debugf logs the given format string/arguments at the DEBUG log level.
  127. func Debugf(format string, a ...interface{}) {
  128. if enableLogging && logLevel >= LevelDebug {
  129. msg := fmt.Sprintf(format, a...)
  130. log.Print("[DEBUG]: " + msg)
  131. }
  132. }
  133. // ElideError transforms the string representation of the provided error
  134. // based on the unsafeLogging setting. Callers that wish to log errors
  135. // returned from Go's net package should use ElideError to sanitize the
  136. // contents first.
  137. func ElideError(err error) string {
  138. // Go's net package is somewhat rude and includes IP address and port
  139. // information in the string representation of net.Errors. Figure out if
  140. // this is the case here, and sanitize the error messages as needed.
  141. if unsafeLogging {
  142. return err.Error()
  143. }
  144. // If err is not a net.Error, just return the string representation,
  145. // presumably transport authors know what they are doing.
  146. netErr, ok := err.(net.Error)
  147. if !ok {
  148. return err.Error()
  149. }
  150. switch t := netErr.(type) {
  151. case *net.AddrError:
  152. return t.Err + " " + elidedAddr
  153. case *net.DNSError:
  154. return "lookup " + elidedAddr + " on " + elidedAddr + ": " + t.Err
  155. case *net.InvalidAddrError:
  156. return "invalid address error"
  157. case *net.UnknownNetworkError:
  158. return "unknown network " + elidedAddr
  159. case *net.OpError:
  160. return t.Op + ": " + t.Err.Error()
  161. default:
  162. // For unknown error types, do the conservative thing and only log the
  163. // type of the error instead of assuming that the string representation
  164. // does not contain sensitive information.
  165. return fmt.Sprintf("network error: <%T>", t)
  166. }
  167. }
  168. // ElideAddr transforms the string representation of the provided address based
  169. // on the unsafeLogging setting. Callers that wish to log IP addreses should
  170. // use ElideAddr to sanitize the contents first.
  171. func ElideAddr(addrStr string) string {
  172. if unsafeLogging {
  173. return addrStr
  174. }
  175. // Only scrub off the address so that it's easier to track connections
  176. // in logs by looking at the port.
  177. if _, port, err := net.SplitHostPort(addrStr); err == nil {
  178. return elidedAddr + ":" + port
  179. }
  180. return elidedAddr
  181. }