-
Notifications
You must be signed in to change notification settings - Fork 45
/
syslog.go
158 lines (135 loc) · 3.32 KB
/
syslog.go
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
package log
import (
"net"
"strconv"
"sync"
"sync/atomic"
"time"
"unsafe"
)
// SyslogWriter is an Writer that writes logs to a syslog server..
type SyslogWriter struct {
// Network specifies network of the syslog server
Network string
// Address specifies address of the syslog server
Address string
// Hostname specifies hostname of the syslog message
Hostname string
// Tag specifies tag of the syslog message
Tag string
// Marker specifies prefix of the syslog message, e.g. `@cee:`
Marker string
// Dial specifies the dial function for creating TCP/TLS connections.
Dial func(network, addr string) (net.Conn, error)
mu sync.Mutex
conn *net.Conn
local bool
}
// Close closes a connection to the syslog server.
func (w *SyslogWriter) Close() (err error) {
w.mu.Lock()
defer w.mu.Unlock()
if w.conn != nil {
err = (*w.conn).Close()
w.conn = nil
return
}
return
}
// connect makes a connection to the syslog server.
func (w *SyslogWriter) connect() (err error) {
if w.conn != nil {
(*w.conn).Close()
w.conn = nil
}
var dial = w.Dial
if dial == nil {
dial = net.Dial
}
var conn net.Conn
conn, err = dial(w.Network, w.Address)
if err != nil {
return
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&w.conn)), unsafe.Pointer(&conn))
w.local = w.Address != "" && w.Address[0] == '/'
if w.Hostname == "" {
if w.local {
w.Hostname = hostname
} else {
w.Hostname, _, _ = net.SplitHostPort((*w.conn).LocalAddr().String())
}
}
return
}
// WriteEntry implements Writer, sends logs with priority to the syslog server.
func (w *SyslogWriter) WriteEntry(e *Entry) (n int, err error) {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&w.conn))) == nil {
w.mu.Lock()
if w.conn == nil {
err = w.connect()
if err != nil {
w.mu.Unlock()
return
}
}
w.mu.Unlock()
}
// convert level to syslog priority
var priority byte
switch e.Level {
case TraceLevel:
priority = '7' // LOG_DEBUG
case DebugLevel:
priority = '7' // LOG_DEBUG
case InfoLevel:
priority = '6' // LOG_INFO
case WarnLevel:
priority = '4' // LOG_WARNING
case ErrorLevel:
priority = '3' // LOG_ERR
case FatalLevel:
priority = '2' // LOG_CRIT
case PanicLevel:
priority = '1' // LOG_ALERT
default:
priority = '6' // LOG_INFO
}
e1 := epool.Get().(*Entry)
defer func(entry *Entry) {
if cap(entry.buf) <= bbcap {
epool.Put(entry)
}
}(e1)
// <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
e1.buf = append(e1.buf[:0], '<', priority, '>')
if w.local {
// Compared to the network form below, the changes are:
// 1. Use time.Stamp instead of time.RFC3339.
// 2. Drop the hostname field.
e1.buf = timeNow().AppendFormat(e1.buf, time.Stamp)
} else {
e1.buf = timeNow().AppendFormat(e1.buf, time.RFC3339)
e1.buf = append(e1.buf, ' ')
e1.buf = append(e1.buf, w.Hostname...)
}
e1.buf = append(e1.buf, ' ')
e1.buf = append(e1.buf, w.Tag...)
e1.buf = append(e1.buf, '[')
e1.buf = strconv.AppendInt(e1.buf, int64(pid), 10)
e1.buf = append(e1.buf, ']', ':', ' ')
e1.buf = append(e1.buf, w.Marker...)
e1.buf = append(e1.buf, e.buf...)
w.mu.Lock()
defer w.mu.Unlock()
if w.conn != nil {
if n, err := (*w.conn).Write(e1.buf); err == nil {
return n, err
}
}
if err := w.connect(); err != nil {
return 0, err
}
return (*w.conn).Write(e1.buf)
}
var _ Writer = (*SyslogWriter)(nil)