log.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*
  2. * Copyright (c) 2016, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package server
  20. import (
  21. "fmt"
  22. "io"
  23. "os"
  24. "github.com/Psiphon-Inc/logrus"
  25. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
  26. )
  27. // ContextLogger adds context logging functionality to the
  28. // underlying logging packages.
  29. type ContextLogger struct {
  30. *logrus.Logger
  31. }
  32. // LogFields is an alias for the field struct in the
  33. // underlying logging package.
  34. type LogFields logrus.Fields
  35. // WithContext adds a "context" field containing the caller's
  36. // function name and source file line number. Use this function
  37. // when the log has no fields.
  38. func (logger *ContextLogger) WithContext() *logrus.Entry {
  39. return log.WithFields(
  40. logrus.Fields{
  41. "context": psiphon.GetParentContext(),
  42. })
  43. }
  44. // WithContextFields adds a "context" field containing the caller's
  45. // function name and source file line number. Use this function
  46. // when the log has fields. Note that any existing "context" field
  47. // will be renamed to "field.context".
  48. func (logger *ContextLogger) WithContextFields(fields LogFields) *logrus.Entry {
  49. _, ok := fields["context"]
  50. if ok {
  51. fields["fields.context"] = fields["context"]
  52. }
  53. fields["context"] = psiphon.GetParentContext()
  54. return log.WithFields(logrus.Fields(fields))
  55. }
  56. // NewLogWriter returns an io.PipeWriter that can be used to write
  57. // to the global logger. Caller must Close() the writer.
  58. func NewLogWriter() *io.PipeWriter {
  59. return log.Writer()
  60. }
  61. // LogFail2Ban logs a message using the format specified by
  62. // config.Fail2BanFormat and the given client IP address. This
  63. // is for integration with fail2ban for blocking abusive
  64. // clients by source IP address.
  65. func LogFail2Ban(clientIPAddress string) {
  66. fail2BanLogger.Info(
  67. fmt.Sprintf(fail2BanFormat, clientIPAddress))
  68. }
  69. var log *ContextLogger
  70. var fail2BanFormat string
  71. var fail2BanLogger *logrus.Logger
  72. // InitLogging configures a logger according to the specified
  73. // config params. If not called, the default logger set by the
  74. // package init() is used.
  75. // When configured, InitLogging also establishes a local syslog
  76. // logger specifically for fail2ban integration.
  77. // Concurrenty note: should only be called from the main
  78. // goroutine.
  79. func InitLogging(config *Config) error {
  80. level, err := logrus.ParseLevel(config.LogLevel)
  81. if err != nil {
  82. return psiphon.ContextError(err)
  83. }
  84. logWriter := os.Stderr
  85. if config.LogFilename != "" {
  86. logWriter, err = os.OpenFile(
  87. config.LogFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
  88. if err != nil {
  89. return psiphon.ContextError(err)
  90. }
  91. }
  92. log = &ContextLogger{
  93. &logrus.Logger{
  94. Out: logWriter,
  95. Formatter: &logrus.JSONFormatter{},
  96. Level: level,
  97. },
  98. }
  99. if config.Fail2BanFormat != "" {
  100. fail2BanFormat = config.Fail2BanFormat
  101. fail2BanLogWriter := os.Stderr
  102. if config.Fail2BanLogFilename != "" {
  103. logWriter, err = os.OpenFile(
  104. config.Fail2BanLogFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
  105. if err != nil {
  106. return psiphon.ContextError(err)
  107. }
  108. }
  109. fail2BanLogger = &logrus.Logger{
  110. Out: fail2BanLogWriter,
  111. Formatter: &logrus.TextFormatter{
  112. DisableColors: true,
  113. FullTimestamp: true,
  114. },
  115. Level: level,
  116. }
  117. }
  118. return nil
  119. }
  120. func init() {
  121. log = &ContextLogger{
  122. &logrus.Logger{
  123. Out: os.Stderr,
  124. Formatter: &logrus.JSONFormatter{},
  125. Hooks: make(logrus.LevelHooks),
  126. Level: logrus.DebugLevel,
  127. },
  128. }
  129. }