le.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Package le_go provides a Golang client library for logging to
  2. // logentries.com over a TCP connection.
  3. //
  4. // it uses an access token for sending log events.
  5. package le_go
  6. import (
  7. "crypto/tls"
  8. "fmt"
  9. "net"
  10. "os"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. // Logger represents a Logentries logger,
  16. // it holds the open TCP connection, access token, prefix and flags.
  17. //
  18. // all Logger operations are thread safe and blocking,
  19. // log operations can be invoked in a non-blocking way by calling them from
  20. // a goroutine.
  21. type Logger struct {
  22. conn net.Conn
  23. flag int
  24. mu sync.Mutex
  25. prefix string
  26. token string
  27. buf []byte
  28. }
  29. const lineSep = "\n"
  30. // Connect creates a new Logger instance and opens a TCP connection to
  31. // logentries.com,
  32. // The token can be generated at logentries.com by adding a new log,
  33. // choosing manual configuration and token based TCP connection.
  34. func Connect(token string) (*Logger, error) {
  35. logger := Logger{
  36. token: token,
  37. }
  38. if err := logger.openConnection(); err != nil {
  39. return nil, err
  40. }
  41. return &logger, nil
  42. }
  43. // Close closes the TCP connection to logentries.com
  44. func (logger *Logger) Close() error {
  45. if logger.conn != nil {
  46. return logger.conn.Close()
  47. }
  48. return nil
  49. }
  50. // Opens a TCP connection to logentries.com
  51. func (logger *Logger) openConnection() error {
  52. conn, err := tls.Dial("tcp", "data.logentries.com:443", &tls.Config{})
  53. if err != nil {
  54. return err
  55. }
  56. logger.conn = conn
  57. return nil
  58. }
  59. // It returns if the TCP connection to logentries.com is open
  60. func (logger *Logger) isOpenConnection() bool {
  61. if logger.conn == nil {
  62. return false
  63. }
  64. buf := make([]byte, 1)
  65. logger.conn.SetReadDeadline(time.Now())
  66. _, err := logger.conn.Read(buf)
  67. switch err.(type) {
  68. case net.Error:
  69. if err.(net.Error).Timeout() == true {
  70. logger.conn.SetReadDeadline(time.Time{})
  71. return true
  72. }
  73. }
  74. return false
  75. }
  76. // It ensures that the TCP connection to logentries.com is open.
  77. // If the connection is closed, a new one is opened.
  78. func (logger *Logger) ensureOpenConnection() error {
  79. if !logger.isOpenConnection() {
  80. if err := logger.openConnection(); err != nil {
  81. return err
  82. }
  83. }
  84. return nil
  85. }
  86. // Fatal is same as Print() but calls to os.Exit(1)
  87. func (logger *Logger) Fatal(v ...interface{}) {
  88. logger.Output(2, fmt.Sprint(v...))
  89. os.Exit(1)
  90. }
  91. // Fatalf is same as Printf() but calls to os.Exit(1)
  92. func (logger *Logger) Fatalf(format string, v ...interface{}) {
  93. logger.Output(2, fmt.Sprintf(format, v...))
  94. os.Exit(1)
  95. }
  96. // Fatalln is same as Println() but calls to os.Exit(1)
  97. func (logger *Logger) Fatalln(v ...interface{}) {
  98. logger.Output(2, fmt.Sprintln(v...))
  99. os.Exit(1)
  100. }
  101. // Flags returns the logger flags
  102. func (logger *Logger) Flags() int {
  103. return logger.flag
  104. }
  105. // Output does the actual writing to the TCP connection
  106. func (logger *Logger) Output(calldepth int, s string) error {
  107. var (
  108. err error
  109. waitPeriod = time.Millisecond
  110. )
  111. for {
  112. _, err = logger.Write([]byte(s))
  113. if err != nil {
  114. if connectionErr := logger.openConnection(); connectionErr != nil {
  115. return connectionErr
  116. }
  117. waitPeriod *= 2
  118. time.Sleep(waitPeriod)
  119. continue
  120. }
  121. return err
  122. }
  123. }
  124. // Panic is same as Print() but calls to panic
  125. func (logger *Logger) Panic(v ...interface{}) {
  126. s := fmt.Sprint(v...)
  127. logger.Output(2, s)
  128. panic(s)
  129. }
  130. // Panicf is same as Printf() but calls to panic
  131. func (logger *Logger) Panicf(format string, v ...interface{}) {
  132. s := fmt.Sprintf(format, v...)
  133. logger.Output(2, s)
  134. panic(s)
  135. }
  136. // Panicln is same as Println() but calls to panic
  137. func (logger *Logger) Panicln(v ...interface{}) {
  138. s := fmt.Sprintln(v...)
  139. logger.Output(2, s)
  140. panic(s)
  141. }
  142. // Prefix returns the logger prefix
  143. func (logger *Logger) Prefix() string {
  144. return logger.prefix
  145. }
  146. // Print logs a message
  147. func (logger *Logger) Print(v ...interface{}) error {
  148. return logger.Output(2, fmt.Sprint(v...))
  149. }
  150. // Printf logs a formatted message
  151. func (logger *Logger) Printf(format string, v ...interface{}) error {
  152. return logger.Output(2, fmt.Sprintf(format, v...))
  153. }
  154. // Println logs a message with a linebreak
  155. func (logger *Logger) Println(v ...interface{}) error {
  156. return logger.Output(2, fmt.Sprintln(v...))
  157. }
  158. // SetFlags sets the logger flags
  159. func (logger *Logger) SetFlags(flag int) {
  160. logger.flag = flag
  161. }
  162. // SetPrefix sets the logger prefix
  163. func (logger *Logger) SetPrefix(prefix string) {
  164. logger.prefix = prefix
  165. }
  166. // Write writes a bytes array to the Logentries TCP connection,
  167. // it adds the access token and prefix and also replaces
  168. // line breaks with the unicode \u2028 character
  169. func (logger *Logger) Write(p []byte) (n int, err error) {
  170. logger.mu.Lock()
  171. if err := logger.ensureOpenConnection(); err != nil {
  172. return 0, err
  173. }
  174. defer logger.mu.Unlock()
  175. logger.makeBuf(p)
  176. return logger.conn.Write(logger.buf)
  177. }
  178. // makeBuf constructs the logger buffer
  179. // it is not safe to be used from within multiple concurrent goroutines
  180. func (logger *Logger) makeBuf(p []byte) {
  181. count := strings.Count(string(p), lineSep)
  182. p = []byte(strings.Replace(string(p), lineSep, "\u2028", count-1))
  183. logger.buf = logger.buf[:0]
  184. logger.buf = append(logger.buf, (logger.token + " ")...)
  185. logger.buf = append(logger.buf, (logger.prefix + " ")...)
  186. logger.buf = append(logger.buf, p...)
  187. if !strings.HasSuffix(string(logger.buf), lineSep) {
  188. logger.buf = append(logger.buf, (lineSep)...)
  189. }
  190. }