le.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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. _, err := logger.Write([]byte(s))
  108. return err
  109. }
  110. // Panic is same as Print() but calls to panic
  111. func (logger *Logger) Panic(v ...interface{}) {
  112. s := fmt.Sprint(v...)
  113. logger.Output(2, s)
  114. panic(s)
  115. }
  116. // Panicf is same as Printf() but calls to panic
  117. func (logger *Logger) Panicf(format string, v ...interface{}) {
  118. s := fmt.Sprintf(format, v...)
  119. logger.Output(2, s)
  120. panic(s)
  121. }
  122. // Panicln is same as Println() but calls to panic
  123. func (logger *Logger) Panicln(v ...interface{}) {
  124. s := fmt.Sprintln(v...)
  125. logger.Output(2, s)
  126. panic(s)
  127. }
  128. // Prefix returns the logger prefix
  129. func (logger *Logger) Prefix() string {
  130. return logger.prefix
  131. }
  132. // Print logs a message
  133. func (logger *Logger) Print(v ...interface{}) {
  134. logger.Output(2, fmt.Sprint(v...))
  135. }
  136. // Printf logs a formatted message
  137. func (logger *Logger) Printf(format string, v ...interface{}) {
  138. logger.Output(2, fmt.Sprintf(format, v...))
  139. }
  140. // Println logs a message with a linebreak
  141. func (logger *Logger) Println(v ...interface{}) {
  142. logger.Output(2, fmt.Sprintln(v...))
  143. }
  144. // SetFlags sets the logger flags
  145. func (logger *Logger) SetFlags(flag int) {
  146. logger.flag = flag
  147. }
  148. // SetPrefix sets the logger prefix
  149. func (logger *Logger) SetPrefix(prefix string) {
  150. logger.prefix = prefix
  151. }
  152. // Write writes a bytes array to the Logentries TCP connection,
  153. // it adds the access token and prefix and also replaces
  154. // line breaks with the unicode \u2028 character
  155. func (logger *Logger) Write(p []byte) (n int, err error) {
  156. if err := logger.ensureOpenConnection(); err != nil {
  157. return 0, err
  158. }
  159. logger.mu.Lock()
  160. defer logger.mu.Unlock()
  161. logger.makeBuf(p)
  162. return logger.conn.Write(logger.buf)
  163. }
  164. // makeBuf constructs the logger buffer
  165. // it is not safe to be used from within multiple concurrent goroutines
  166. func (logger *Logger) makeBuf(p []byte) {
  167. count := strings.Count(string(p), lineSep)
  168. p = []byte(strings.Replace(string(p), lineSep, "\u2028", count-1))
  169. logger.buf = logger.buf[:0]
  170. logger.buf = append(logger.buf, (logger.token + " ")...)
  171. logger.buf = append(logger.buf, (logger.prefix + " ")...)
  172. logger.buf = append(logger.buf, p...)
  173. if !strings.HasSuffix(string(logger.buf), lineSep) {
  174. logger.buf = append(logger.buf, (lineSep)...)
  175. }
  176. }