Skip to content

Instantly share code, notes, and snippets.

@cpuguy83
Last active August 7, 2020 06:37
Show Gist options
  • Save cpuguy83/b7c0f42e903bc13c46d6 to your computer and use it in GitHub Desktop.
Save cpuguy83/b7c0f42e903bc13c46d6 to your computer and use it in GitHub Desktop.
libswarm logger demo
package backends
import (
"fmt"
"io"
"strings"
"sync"
"time"
"github.com/docker/libswarm"
"github.com/docker/libswarm/utils"
)
type logForwarder struct {
service *libswarm.Server
dockerInstances map[string]struct{}
logFacility libswarm.Sender
}
// This attaches to any/all containers and gets the stdout/stderr streams from them.
//TODO: decouple from dockerclient backend
func LogForwarder() libswarm.Sender {
l := &logForwarder{
service: libswarm.NewServer(),
dockerInstances: map[string]struct{}{},
}
l.service.OnLog(l.log)
l.service.OnVerb(libswarm.Spawn, libswarm.Handler(l.spawn))
l.service.OnStart(l.start)
return l.service
}
func (l *logForwarder) spawn(msg *libswarm.Message) (err error) {
for _, host := range msg.Args {
l.dockerInstances[host] = struct{}{}
}
instance := utils.Task(func(in libswarm.Receiver, out libswarm.Sender) {
l.logFacility = out
})
msg.Ret.Send(&libswarm.Message{
Verb: libswarm.Ack,
Ret: instance,
})
return libswarm.AsClient(l.service).Start()
}
func (l *logForwarder) log(msg ...string) error {
libswarm.AsClient(l.logFacility).Log(strings.Join(msg, "\t"))
return nil
}
func (l *logForwarder) start() error {
return l.getAllLogs()
}
func (l *logForwarder) getContainerLog(client *libswarm.Client, host, name string) error {
_, out, err := client.Attach(name)
if err != nil {
return err
}
c := libswarm.AsClient(out)
logs, _, err := c.Attach("")
if err != nil {
return err
}
prefix := []string{host, name}
var tasks sync.WaitGroup
go func() {
defer tasks.Done()
err := l.DecodeStream(logs, "stdout", prefix)
if err != nil {
fmt.Printf("decodestream: %v\n", err)
}
}()
tasks.Add(1)
go func() {
defer tasks.Done()
err := l.DecodeStream(logs, "stderr", prefix)
if err != nil {
fmt.Printf("decodestream: %v\n", err)
}
}()
tasks.Add(1)
tasks.Wait()
fmt.Println("Stopped logging", name)
return nil
}
func (l *logForwarder) getAllLogs() error {
dockerBackend := libswarm.AsClient(DockerClient())
for host := range l.dockerInstances {
b, err := dockerBackend.Spawn(host)
if err != nil {
fmt.Errorf("Could not spawn %s", host)
}
backend := libswarm.AsClient(b)
names, err := backend.Ls()
if err != nil {
return err
}
fmt.Println("Getting logs", backend, names) // DEBUG
for _, name := range names {
go l.getContainerLog(backend, host, name)
}
}
return nil
}
func (l *logForwarder) DecodeStream(src libswarm.Receiver, tag string, prefix []string) error {
dst := libswarm.AsClient(l.service)
for {
msg, err := src.Receive(libswarm.Ret)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if tag == msg.Args[0] {
var logTag string
switch tag {
case "stdout":
logTag = "INFO"
case "stderr":
logTag = "ERROR"
}
logEntry := fmt.Sprintf("%s\t%s\t%s\t%s", time.Now(), strings.Join(prefix, "\t"), logTag, msg.Args[1])
if err := dst.Log(logEntry); err != nil {
return err
}
}
}
}
package backends
import (
"fmt"
"strings"
"github.com/docker/libswarm"
)
type stdoutLogger struct {
*libswarm.Server
}
func StdoutLogger() libswarm.Sender {
backend := libswarm.NewServer()
backend.OnSpawn(func(cmd ...string) (libswarm.Sender, error) {
fl := &stdoutLogger{Server: libswarm.NewServer()}
fl.OnAttach(fl.attach)
fl.OnStart(fl.start)
fl.OnLog(fl.log)
return fl, nil
})
return backend
}
func (l *stdoutLogger) attach(name string, ret libswarm.Sender) error {
ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: l.Server})
<-make(chan struct{})
return nil
}
func (l *stdoutLogger) start() error {
fmt.Errorf("logger: start not implemented")
return nil
}
func (l *stdoutLogger) log(msg ...string) error {
fmt.Println(strings.Join(msg, "\t"))
return nil
}
@rgbkrk
Copy link

rgbkrk commented Aug 2, 2014

Where is DockerClient defined? I see it used on Line 94 (at this time), but when trying to go get this example it comes up undefined. Do I need to do more finagling?

$ go get gist.github.com/b7c0f42e903bc13c46d6.git
# gist.github.com/b7c0f42e903bc13c46d6.git
../../../gist.github.com/b7c0f42e903bc13c46d6.git/logforwarder.go:94: undefined: DockerClient

@aaronfeng
Copy link

@rgbkrk https://github.com/docker/libswarm/blob/master/backends/dockerclient.go It might be easier if you clone libswarm and copy the files into backends dir. A little dirty, but should get you going.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment