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