Skip to content

Commit 7701372

Browse files
Gabriel MonroyMatthew Fisher
authored andcommitted
feat(logger): add new syslog daemon based on https://github.com/ziutek/syslog
1 parent b8ced1b commit 7701372

18 files changed

Lines changed: 656 additions & 147 deletions

logger/Dockerfile

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
FROM deis/base:latest
22
MAINTAINER Gabriel Monroy <gabriel@opdemand.com>
33

4-
# install rsyslog from rsyslog.com
5-
RUN echo "deb http://ppa.launchpad.net/adiscon/v7-stable/ubuntu precise main" > /etc/apt/sources.list.d/rsyslog.list
6-
RUN echo "deb-src http://ppa.launchpad.net/adiscon/v7-stable/ubuntu precise main" >> /etc/apt/sources.list.d/rsyslog.list
7-
RUN gpg --recv-keys --keyserver keyserver.ubuntu.com AEF0CF8E
8-
RUN gpg --export --armor AEF0CF8E | sudo apt-key add -
9-
RUN apt-get update
10-
RUN apt-get install -yq --force-yes rsyslog
4+
# install go runtime
5+
RUN wget -O /tmp/go1.2.1.linux-amd64.tar.gz -q https://go.googlecode.com/files/go1.2.1.linux-amd64.tar.gz
6+
RUN tar -C /usr/local -xzvf /tmp/go1.2.1.linux-amd64.tar.gz
117

12-
# create /var/log/deis for holding logs (access via bind mount)
13-
RUN mkdir -p /var/log/deis
14-
15-
# install latest etcdctl including no-sync options
16-
RUN wget -q https://s3-us-west-2.amazonaws.com/deis/etcdctl.no-sync -O /usr/local/bin/etcdctl
17-
RUN chmod +x /usr/local/bin/etcdctl
8+
# prepare go environment
9+
RUN mkdir -p /go
10+
ENV GOPATH /go
11+
ENV PATH /usr/local/bin:/usr/bin:/bin:/sbin:/usr/local/go/bin
1812

1913
# add the current build context to /app
2014
ADD . /app
15+
ADD syslog /go/src/syslog
16+
ADD syslogd /go/src/syslogd
17+
18+
# compile the binary
19+
RUN cd /go/src/syslogd && go install -v syslogd
20+
21+
# create /var/log/deis for holding logs (access via bind mount)
22+
RUN mkdir -p /var/log/deis
2123

2224
# prepare execution environment
2325
WORKDIR /app

logger/bin/boot

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,11 @@ export ETCD="$HOST:$ETCD_PORT"
1515
export ETCD_PATH=${ETCD_PATH:-/deis/logs}
1616
export ETCD_TTL=${ETCD_TTL:-10}
1717

18-
# wait for etcd to be available
19-
until etcdctl --no-sync -C $ETCD ls >/dev/null; do
20-
echo "waiting for etcd at $ETCD..."
21-
sleep $(($ETCD_TTL/2)) # sleep for half the TTL
22-
done
23-
24-
# wait until etcd has discarded potentially stale values
25-
sleep $(($ETCD_TTL+1))
26-
27-
# seed initial service configuration if necessary
28-
if ! etcdctl --no-sync -C $ETCD ls $ETCD_PATH >/dev/null 2>&1; then
29-
etcdctl --no-sync -C $ETCD mkdir $ETCD_PATH >/dev/null 2>&1 || true
30-
fi
31-
32-
# wait for confd to run once and install initial templates
33-
until confd -onetime -node $ETCD -config-file /app/confd.toml; do
34-
echo "logger: waiting for confd to write initial templates..."
35-
sleep $(($ETCD_TTL/2)) # sleep for half the TTL
36-
done
37-
38-
# cleanup old pidfile's
39-
test -e /var/run/rsyslog.pid && rm -f /var/run/rsyslog.pid
18+
# fix perms on log directory
19+
chmod 755 /var/log/deis
4020

4121
# spawn the service in the background
42-
/usr/sbin/rsyslogd -n &
22+
/go/bin/syslogd &
4323
SERVICE_PID=$!
4424

4525
# smart shutdown on SIGINT and SIGTERM
@@ -49,10 +29,6 @@ function on_exit() {
4929
}
5030
trap on_exit INT TERM
5131

52-
# spawn confd in the background to update services based on etcd changes
53-
confd -node $ETCD -config-file /app/confd.toml &
54-
CONFD_PID=$!
55-
5632
echo deis-logger running...
5733

5834
# publish the service to etcd using the injected PORT

logger/conf.d/default.toml

Lines changed: 0 additions & 6 deletions
This file was deleted.

logger/conf.d/deis.conf.toml

Lines changed: 0 additions & 6 deletions
This file was deleted.

logger/conf.d/rsyslog.conf.toml

Lines changed: 0 additions & 6 deletions
This file was deleted.

logger/confd.toml

Lines changed: 0 additions & 5 deletions
This file was deleted.

logger/syslog/LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2012, Michal Derkacz
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions
6+
are met:
7+
1. Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
2. Redistributions in binary form must reproduce the above copyright
10+
notice, this list of conditions and the following disclaimer in the
11+
documentation and/or other materials provided with the distribution.
12+
3. The name of the author may not be used to endorse or promote products
13+
derived from this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18+
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

logger/syslog/filehandler.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package syslog
2+
3+
import (
4+
"log"
5+
"os"
6+
"os/signal"
7+
"syscall"
8+
)
9+
10+
// FileHandler implements Handler interface in the way to save messages into a
11+
// text file. It properly handles logrotate HUP signal (closes a file and tries
12+
// to open/create new one).
13+
type FileHandler struct {
14+
bh *BaseHandler
15+
filename string
16+
f *os.File
17+
l Logger
18+
}
19+
20+
// NewFileHandler accepts all arguments expected by NewBaseHandler plus
21+
// filename which is the path to the log file.
22+
func NewFileHandler(filename string, qlen int, filter func(*Message) bool,
23+
ft bool) *FileHandler {
24+
25+
h := &FileHandler{
26+
bh: NewBaseHandler(qlen, filter, ft),
27+
filename: filename,
28+
l: log.New(os.Stderr, "", log.LstdFlags),
29+
}
30+
go h.mainLoop()
31+
return h
32+
}
33+
34+
// SetLogger changes an internal logger used to log I/O errors. By default I/O
35+
// errors are written to os.Stderr using log.Logger.
36+
func (h *FileHandler) SetLogger(l Logger) {
37+
h.l = l
38+
}
39+
40+
func (h *FileHandler) mainLoop() {
41+
defer h.bh.End()
42+
43+
mq := h.bh.Queue()
44+
sq := make(chan os.Signal, 1)
45+
signal.Notify(sq, syscall.SIGHUP)
46+
47+
for {
48+
select {
49+
case <-sq: // SIGHUP probably from logrotate
50+
h.checkErr(h.f.Close())
51+
h.f = nil
52+
case m, ok := <-mq: // message to save
53+
if !ok {
54+
if h.f != nil {
55+
h.checkErr(h.f.Close())
56+
}
57+
return
58+
}
59+
h.saveMessage(m)
60+
}
61+
}
62+
}
63+
64+
func (h *FileHandler) saveMessage(m *Message) {
65+
var err error
66+
if h.f == nil {
67+
h.f, err = os.OpenFile(
68+
h.filename,
69+
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
70+
0620,
71+
)
72+
if h.checkErr(err) {
73+
return
74+
}
75+
}
76+
_, err = h.f.WriteString(m.String() + "\n")
77+
h.checkErr(err)
78+
}
79+
80+
func (h *FileHandler) checkErr(err error) bool {
81+
if err == nil {
82+
return false
83+
}
84+
if h.l == nil {
85+
log.Print(h.filename, ": ", err)
86+
} else {
87+
h.l.Print(h.filename, ": ", err)
88+
}
89+
return true
90+
}
91+
92+
func (h *FileHandler) Handle(m *Message) *Message {
93+
return h.bh.Handle(m)
94+
}

logger/syslog/handler.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package syslog
2+
3+
// Handler handles syslog messages
4+
type Handler interface {
5+
// Handle should return Message (mayby modified) for future processing by
6+
// other handlers or return nil. If Handle is called with nil message it
7+
// should complete all remaining work and properly shutdown before return.
8+
Handle(*Message) *Message
9+
}
10+
11+
// BaseHandler is desigend for simplify the creation of real handlers. It
12+
// implements Handler interface using nonblocking queuing of messages and
13+
// simple message filtering.
14+
type BaseHandler struct {
15+
queue chan *Message
16+
end chan struct{}
17+
filter func(*Message) bool
18+
ft bool
19+
}
20+
21+
// NewBaseHandler creates BaseHandler using specified filter. If filter is nil
22+
// or if it returns true messages are passed to BaseHandler internal queue
23+
// (of qlen length). If filter returns false or ft is true messages are returned
24+
// to server for future processing by other handlers.
25+
func NewBaseHandler(qlen int, filter func(*Message) bool, ft bool) *BaseHandler {
26+
return &BaseHandler{
27+
queue: make(chan *Message, qlen),
28+
end: make(chan struct{}),
29+
filter: filter,
30+
ft: ft,
31+
}
32+
}
33+
34+
// Handle inserts m in an internal queue. It immediately returns even if
35+
// queue is full. If m == nil it closes queue and waits for End method call
36+
// before return.
37+
func (h *BaseHandler) Handle(m *Message) *Message {
38+
if m == nil {
39+
close(h.queue) // signal that ther is no more messages for processing
40+
<-h.end // wait for handler shutdown
41+
return nil
42+
}
43+
if h.filter != nil && !h.filter(m) {
44+
// m doesn't match the filter
45+
return m
46+
}
47+
// Try queue m
48+
select {
49+
case h.queue <- m:
50+
default:
51+
}
52+
if h.ft {
53+
return m
54+
}
55+
return nil
56+
}
57+
58+
// Get returns first message from internal queue. It waits for message if queue
59+
// is empty. It returns nil if there is no more messages to process and handler
60+
// should shutdown.
61+
func (h *BaseHandler) Get() *Message {
62+
m, ok := <-h.queue
63+
if ok {
64+
return m
65+
}
66+
return nil
67+
}
68+
69+
// Queue returns BaseHandler internal queue as read-only channel. You can use
70+
// it directly, especially if your handler need to select from multiple channels
71+
// or have to work without blocking. You need to check if this channel is closed by
72+
// sender and properly shutdown in this case.
73+
func (h *BaseHandler) Queue() <-chan *Message {
74+
return h.queue
75+
}
76+
77+
// End signals the server that handler properly shutdown. You need to call End
78+
// only if Get has returned nil before.
79+
func (h *BaseHandler) End() {
80+
close(h.end)
81+
}

logger/syslog/internallog.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package syslog
2+
3+
// Logger is an interface for package internal (non fatal) logging
4+
type Logger interface {
5+
Print(...interface{})
6+
Printf(format string, v ...interface{})
7+
Println(...interface{})
8+
}
9+
10+
// FatalLogger is an interface for logging package internal fatal errors
11+
type FatalLogger interface {
12+
Fatal(...interface{})
13+
Fatalf(format string, v ...interface{})
14+
Fatalln(...interface{})
15+
}

0 commit comments

Comments
 (0)