2016-02-10 02:24:30 +00:00
|
|
|
package syslog
|
|
|
|
|
|
|
|
import (
|
2016-02-10 16:03:31 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/gob"
|
2016-02-10 02:24:30 +00:00
|
|
|
"fmt"
|
2016-02-10 15:52:15 +00:00
|
|
|
"io"
|
2016-02-10 02:24:30 +00:00
|
|
|
"log"
|
|
|
|
"net"
|
2016-02-10 15:52:15 +00:00
|
|
|
"path/filepath"
|
2016-02-10 02:24:30 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/nomad/client/allocdir"
|
|
|
|
"github.com/hashicorp/nomad/client/driver/logrotator"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
"github.com/mcuadros/go-syslog"
|
|
|
|
)
|
|
|
|
|
|
|
|
type LogCollectorContext struct {
|
2016-02-10 15:52:15 +00:00
|
|
|
TaskName string
|
|
|
|
AllocDir *allocdir.AllocDir
|
|
|
|
LogConfig *structs.LogConfig
|
|
|
|
PortUpperBound uint
|
|
|
|
PortLowerBound uint
|
2016-02-10 02:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type SyslogCollectorState struct {
|
|
|
|
IsolationConfig *IsolationConfig
|
2016-02-10 15:52:15 +00:00
|
|
|
Addr string
|
2016-02-10 02:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type LogCollector interface {
|
2016-02-10 15:52:15 +00:00
|
|
|
LaunchCollector(ctx *LogCollectorContext) (*SyslogCollectorState, error)
|
2016-02-10 02:24:30 +00:00
|
|
|
Exit() error
|
|
|
|
UpdateLogConfig(logConfig *structs.LogConfig) error
|
|
|
|
}
|
|
|
|
|
|
|
|
type IsolationConfig struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
type SyslogCollector struct {
|
|
|
|
addr net.Addr
|
|
|
|
logConfig *structs.LogConfig
|
|
|
|
ctx *LogCollectorContext
|
|
|
|
|
|
|
|
lro *logrotator.LogRotator
|
|
|
|
lre *logrotator.LogRotator
|
|
|
|
server *syslog.Server
|
|
|
|
taskDir string
|
|
|
|
|
|
|
|
logger *log.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSyslogCollector(logger *log.Logger) *SyslogCollector {
|
|
|
|
return &SyslogCollector{logger: logger}
|
|
|
|
}
|
|
|
|
|
2016-02-10 15:52:15 +00:00
|
|
|
func (s *SyslogCollector) LaunchCollector(ctx *LogCollectorContext) (*SyslogCollectorState, error) {
|
|
|
|
addr, err := s.getFreePort(ctx.PortLowerBound, ctx.PortUpperBound)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-10 02:24:30 +00:00
|
|
|
s.logger.Printf("sylog-server: launching syslog server on addr: %v", addr)
|
|
|
|
s.ctx = ctx
|
|
|
|
// configuring the task dir
|
|
|
|
if err := s.configureTaskDir(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
channel := make(syslog.LogPartsChannel)
|
|
|
|
handler := syslog.NewChannelHandler(channel)
|
|
|
|
|
|
|
|
s.server = syslog.NewServer()
|
2016-02-10 15:52:15 +00:00
|
|
|
s.server.SetFormat(&CustomParser{logger: s.logger})
|
2016-02-10 02:24:30 +00:00
|
|
|
s.server.SetHandler(handler)
|
|
|
|
s.server.ListenTCP(addr.String())
|
|
|
|
if err := s.server.Boot(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-10 15:52:15 +00:00
|
|
|
r, w := io.Pipe()
|
|
|
|
logFileSize := int64(ctx.LogConfig.MaxFileSizeMB * 1024 * 1024)
|
|
|
|
lro, err := logrotator.NewLogRotator(filepath.Join(s.taskDir, allocdir.TaskLocal),
|
|
|
|
fmt.Sprintf("%v.stdout", ctx.TaskName), ctx.LogConfig.MaxFiles,
|
|
|
|
logFileSize, s.logger)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
go lro.Start(r)
|
2016-02-10 02:24:30 +00:00
|
|
|
|
|
|
|
go func(channel syslog.LogPartsChannel) {
|
|
|
|
for logParts := range channel {
|
2016-02-10 16:03:31 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
enc := gob.NewEncoder(&buf)
|
|
|
|
if err := enc.Encode(logParts["content"]); err == nil {
|
|
|
|
w.Write(buf.Bytes())
|
|
|
|
}
|
2016-02-10 02:24:30 +00:00
|
|
|
}
|
|
|
|
}(channel)
|
|
|
|
go s.server.Wait()
|
2016-02-10 15:52:15 +00:00
|
|
|
return &SyslogCollectorState{Addr: addr.String()}, nil
|
2016-02-10 02:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SyslogCollector) Exit() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SyslogCollector) UpdateLogConfig(logConfig *structs.LogConfig) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// configureTaskDir sets the task dir in the executor
|
|
|
|
func (s *SyslogCollector) configureTaskDir() error {
|
|
|
|
taskDir, ok := s.ctx.AllocDir.TaskDirs[s.ctx.TaskName]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("couldn't find task directory for task %v", s.ctx.TaskName)
|
|
|
|
}
|
|
|
|
s.taskDir = taskDir
|
|
|
|
return nil
|
|
|
|
}
|
2016-02-10 15:52:15 +00:00
|
|
|
|
|
|
|
// getFreePort returns a free port ready to be listened on between upper and
|
|
|
|
// lower bounds
|
|
|
|
func (s *SyslogCollector) getFreePort(lowerBound uint, upperBound uint) (net.Addr, error) {
|
|
|
|
for i := lowerBound; i <= upperBound; i++ {
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("localhost:%v", i))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
l, err := net.ListenTCP("tcp", addr)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
defer l.Close()
|
|
|
|
return l.Addr(), nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("No free port found")
|
|
|
|
}
|