utils.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package types
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/gob"
  6. "fmt"
  7. "io"
  8. "os"
  9. "path/filepath"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. "time"
  14. log "github.com/sirupsen/logrus"
  15. "gopkg.in/natefinch/lumberjack.v2"
  16. )
  17. var logFormatter log.Formatter
  18. var LogOutput *lumberjack.Logger //io.Writer
  19. var logLevel log.Level
  20. func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, compress *bool, forceColors bool) error {
  21. /*Configure logs*/
  22. if cfgMode == "file" {
  23. _maxsize := 500
  24. if maxSize != 0 {
  25. _maxsize = maxSize
  26. }
  27. _maxfiles := 3
  28. if maxFiles != 0 {
  29. _maxfiles = maxFiles
  30. }
  31. _maxage := 28
  32. if maxAge != 0 {
  33. _maxage = maxAge
  34. }
  35. _compress := true
  36. if compress != nil {
  37. _compress = *compress
  38. }
  39. /*cf. https://github.com/natefinch/lumberjack/issues/82
  40. let's create the file beforehand w/ the right perms */
  41. fname := cfgFolder + "/crowdsec.log"
  42. // check if file exists
  43. _, err := os.Stat(fname)
  44. // create file if not exists, purposefully ignore errors
  45. if os.IsNotExist(err) {
  46. file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0600)
  47. file.Close()
  48. }
  49. LogOutput = &lumberjack.Logger{
  50. Filename: fname,
  51. MaxSize: _maxsize,
  52. MaxBackups: _maxfiles,
  53. MaxAge: _maxage,
  54. Compress: _compress,
  55. }
  56. log.SetOutput(LogOutput)
  57. } else if cfgMode != "stdout" {
  58. return fmt.Errorf("log mode '%s' unknown", cfgMode)
  59. }
  60. logLevel = cfgLevel
  61. log.SetLevel(logLevel)
  62. logFormatter = &log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true, ForceColors: forceColors}
  63. log.SetFormatter(logFormatter)
  64. return nil
  65. }
  66. func ConfigureLogger(clog *log.Logger) error {
  67. /*Configure logs*/
  68. if LogOutput != nil {
  69. clog.SetOutput(LogOutput)
  70. }
  71. if logFormatter != nil {
  72. clog.SetFormatter(logFormatter)
  73. }
  74. clog.SetLevel(logLevel)
  75. return nil
  76. }
  77. func Clone(a, b interface{}) error {
  78. buff := new(bytes.Buffer)
  79. enc := gob.NewEncoder(buff)
  80. dec := gob.NewDecoder(buff)
  81. if err := enc.Encode(a); err != nil {
  82. return fmt.Errorf("failed cloning %T", a)
  83. }
  84. if err := dec.Decode(b); err != nil {
  85. return fmt.Errorf("failed cloning %T", b)
  86. }
  87. return nil
  88. }
  89. func ParseDuration(d string) (time.Duration, error) {
  90. durationStr := d
  91. if strings.HasSuffix(d, "d") {
  92. days := strings.Split(d, "d")[0]
  93. if len(days) == 0 {
  94. return 0, fmt.Errorf("'%s' can't be parsed as duration", d)
  95. }
  96. daysInt, err := strconv.Atoi(days)
  97. if err != nil {
  98. return 0, err
  99. }
  100. durationStr = strconv.Itoa(daysInt*24) + "h"
  101. }
  102. duration, err := time.ParseDuration(durationStr)
  103. if err != nil {
  104. return 0, err
  105. }
  106. return duration, nil
  107. }
  108. /*help to copy the file, ioutil doesn't offer the feature*/
  109. func copyFileContents(src, dst string) (err error) {
  110. in, err := os.Open(src)
  111. if err != nil {
  112. return
  113. }
  114. defer in.Close()
  115. out, err := os.Create(dst)
  116. if err != nil {
  117. return
  118. }
  119. defer func() {
  120. cerr := out.Close()
  121. if err == nil {
  122. err = cerr
  123. }
  124. }()
  125. if _, err = io.Copy(out, in); err != nil {
  126. return
  127. }
  128. err = out.Sync()
  129. return
  130. }
  131. /*copy the file, ioutile doesn't offer the feature*/
  132. func CopyFile(sourceSymLink, destinationFile string) (err error) {
  133. sourceFile, err := filepath.EvalSymlinks(sourceSymLink)
  134. if err != nil {
  135. log.Infof("Not a symlink : %s", err)
  136. sourceFile = sourceSymLink
  137. }
  138. sourceFileStat, err := os.Stat(sourceFile)
  139. if err != nil {
  140. return
  141. }
  142. if !sourceFileStat.Mode().IsRegular() {
  143. // cannot copy non-regular files (e.g., directories,
  144. // symlinks, devices, etc.)
  145. return fmt.Errorf("copyFile: non-regular source file %s (%q)", sourceFileStat.Name(), sourceFileStat.Mode().String())
  146. }
  147. destinationFileStat, err := os.Stat(destinationFile)
  148. if err != nil {
  149. if !os.IsNotExist(err) {
  150. return
  151. }
  152. } else {
  153. if !(destinationFileStat.Mode().IsRegular()) {
  154. return fmt.Errorf("copyFile: non-regular destination file %s (%q)", destinationFileStat.Name(), destinationFileStat.Mode().String())
  155. }
  156. if os.SameFile(sourceFileStat, destinationFileStat) {
  157. return
  158. }
  159. }
  160. if err = os.Link(sourceFile, destinationFile); err != nil {
  161. err = copyFileContents(sourceFile, destinationFile)
  162. }
  163. return
  164. }
  165. func UtcNow() time.Time {
  166. return time.Now().UTC()
  167. }
  168. func GetLineCountForFile(filepath string) int {
  169. f, err := os.Open(filepath)
  170. if err != nil {
  171. log.Fatalf("unable to open log file %s : %s", filepath, err)
  172. }
  173. defer f.Close()
  174. lc := 0
  175. fs := bufio.NewScanner(f)
  176. for fs.Scan() {
  177. lc++
  178. }
  179. return lc
  180. }
  181. // from https://github.com/acarl005/stripansi
  182. var reStripAnsi = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
  183. func StripAnsiString(str string) string {
  184. // the byte version doesn't strip correctly
  185. return reStripAnsi.ReplaceAllString(str, "")
  186. }