Merge branch 'master' into api
Signed-off-by: Michael Crosby <crosbymichael@gmail.com> Conflicts: MAINTAINERS cgroups/cgroups.go cgroups/fs/apply_raw.go cgroups/fs/notify_linux.go cgroups/fs/notify_linux_test.go cgroups/systemd/apply_systemd.go config.go configs/config_test.go console/console.go integration/exec_test.go integration/init_test.go integration/template_test.go integration/utils_test.go linux_notify.go linux_notify_test.go mount/init.go mount/mount_config.go mount/pivotroot.go mount/ptmx.go namespaces/create.go namespaces/exec.go namespaces/execin.go namespaces/init.go namespaces/nsenter/nsenter.c namespaces/nsenter/nsenter.go namespaces/utils.go network/network.go network/types.go network/veth.go notify_linux.go notify_linux_test.go nsinit/exec.go nsinit/main.go nsinit/nsenter.go nsinit/oom.go sample_configs/host-pid.json sample_configs/userns.json security/capabilities/capabilities.go update-vendor.sh
This commit is contained in:
commit
f4cf808a3d
|
@ -3,4 +3,5 @@ Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
|||
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
||||
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
||||
Alexandr Morozov <lk4d4@docker.com> (@LK4D4)
|
||||
Daniel, Dao Quang Minh <dqminh89@gmail.com> (@dqminh)
|
||||
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
|
|
1
Makefile
1
Makefile
|
@ -12,7 +12,6 @@ sh:
|
|||
GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u)
|
||||
|
||||
direct-test:
|
||||
go get github.com/golang/glog && \
|
||||
go test $(TEST_TAGS) -cover -v $(GO_PACKAGES)
|
||||
|
||||
direct-test-short:
|
||||
|
|
|
@ -24,7 +24,6 @@ func ApplyProfile(name string) error {
|
|||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
cName := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cName))
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
|
@ -40,20 +40,31 @@ type Manager struct {
|
|||
}
|
||||
|
||||
// The absolute path to the root of the cgroup hierarchies.
|
||||
var cgroupRootLock sync.Mutex
|
||||
var cgroupRoot string
|
||||
|
||||
// TODO(vmarmol): Report error here, we'll probably need to wait for the new API.
|
||||
func init() {
|
||||
// Gets the cgroupRoot.
|
||||
func getCgroupRoot() (string, error) {
|
||||
cgroupRootLock.Lock()
|
||||
defer cgroupRootLock.Unlock()
|
||||
|
||||
if cgroupRoot != "" {
|
||||
return cgroupRoot, nil
|
||||
}
|
||||
|
||||
// we can pick any subsystem to find the root
|
||||
cpuRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
||||
if err != nil {
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
cgroupRoot = filepath.Dir(cpuRoot)
|
||||
root := filepath.Dir(cpuRoot)
|
||||
|
||||
if _, err := os.Stat(cgroupRoot); err != nil {
|
||||
return
|
||||
if _, err := os.Stat(root); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cgroupRoot = root
|
||||
return cgroupRoot, nil
|
||||
}
|
||||
|
||||
type data struct {
|
||||
|
@ -172,8 +183,9 @@ func (m *Manager) GetPids() ([]int, error) {
|
|||
}
|
||||
|
||||
func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
|
||||
if cgroupRoot == "" {
|
||||
return nil, fmt.Errorf("failed to find the cgroup root")
|
||||
root, err := getCgroupRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cgroup := c.Name
|
||||
|
@ -182,7 +194,7 @@ func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
|
|||
}
|
||||
|
||||
return &data{
|
||||
root: cgroupRoot,
|
||||
root: root,
|
||||
cgroup: cgroup,
|
||||
c: c,
|
||||
pid: pid,
|
||||
|
|
|
@ -30,9 +30,10 @@ type subsystem interface {
|
|||
}
|
||||
|
||||
var (
|
||||
connLock sync.Mutex
|
||||
theConn *systemd.Conn
|
||||
hasStartTransientUnit bool
|
||||
connLock sync.Mutex
|
||||
theConn *systemd.Conn
|
||||
hasStartTransientUnit bool
|
||||
hasTransientDefaultDependencies bool
|
||||
)
|
||||
|
||||
func newProp(name string, units interface{}) systemd.Property {
|
||||
|
@ -66,6 +67,18 @@ func UseSystemd() bool {
|
|||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
if dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" {
|
||||
hasStartTransientUnit = false
|
||||
return hasStartTransientUnit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assume StartTransientUnit on a scope allows DefaultDependencies
|
||||
hasTransientDefaultDependencies = true
|
||||
ddf := newProp("DefaultDependencies", false)
|
||||
if _, err := theConn.StartTransientUnit("docker-systemd-test-default-dependencies.scope", "replace", ddf); err != nil {
|
||||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
if dbusError.Name == "org.freedesktop.DBus.Error.PropertyReadOnly" {
|
||||
hasTransientDefaultDependencies = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +121,11 @@ func (m *Manager) Apply(pid int) error {
|
|||
newProp("CPUAccounting", true),
|
||||
newProp("BlockIOAccounting", true))
|
||||
|
||||
if hasTransientDefaultDependencies {
|
||||
properties = append(properties,
|
||||
newProp("DefaultDependencies", false))
|
||||
}
|
||||
|
||||
if c.Memory != 0 {
|
||||
properties = append(properties,
|
||||
newProp("MemoryLimit", uint64(c.Memory)))
|
||||
|
@ -128,14 +146,12 @@ func (m *Manager) Apply(pid int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if !c.AllowAllDevices {
|
||||
if err := joinDevices(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := joinDevices(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// -1 disables memorySwap
|
||||
if c.MemorySwap >= 0 && (c.Memory != 0 || c.MemorySwap > 0) {
|
||||
if c.MemorySwap >= 0 && c.Memory != 0 {
|
||||
if err := joinMemory(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -290,16 +306,16 @@ func joinDevices(c *configs.Cgroup, pid int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := writeFile(path, "devices.deny", "a"); err != nil {
|
||||
return err
|
||||
if !c.AllowAllDevices {
|
||||
if err := writeFile(path, "devices.deny", "a"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, dev := range c.AllowedDevices {
|
||||
if err := writeFile(path, "devices.allow", dev.CgroupString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ func TestHostUIDNoUSERNS(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if uid != 0 {
|
||||
t.Fatal("expected uid 0 with no USERNS but received %d", uid)
|
||||
t.Fatalf("expected uid 0 with no USERNS but received %d", uid)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ func TestHostUIDWithUSERNS(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if uid != 1000 {
|
||||
t.Fatal("expected uid 1000 with no USERNS but received %d", uid)
|
||||
t.Fatalf("expected uid 1000 with no USERNS but received %d", uid)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ func TestHostGIDNoUSERNS(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if uid != 0 {
|
||||
t.Fatal("expected gid 0 with no USERNS but received %d", uid)
|
||||
t.Fatalf("expected gid 0 with no USERNS but received %d", uid)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,6 +215,6 @@ func TestHostGIDWithUSERNS(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
if uid != 1000 {
|
||||
t.Fatal("expected gid 1000 with no USERNS but received %d", uid)
|
||||
t.Fatalf("expected gid 1000 with no USERNS but received %d", uid)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
% nsinit User Manual
|
||||
% docker/libcontainer
|
||||
% JAN 2015
|
||||
|
||||
NAME:
|
||||
nsinit - A low-level utility for managing containers.
|
||||
It is used to spawn new containers or join existing containers.
|
||||
|
||||
USAGE:
|
||||
nsinit [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.1
|
||||
|
||||
COMMANDS:
|
||||
config display the container configuration
|
||||
exec execute a new command inside a container
|
||||
init runs the init process inside the namespace
|
||||
oom display oom notifications for a container
|
||||
pause pause the container's processes
|
||||
stats display statistics for the container
|
||||
unpause unpause the container's processes
|
||||
help, h shows a list of commands or help for one command
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
Get the <container_id> of an already running docker container.
|
||||
`sudo docker ps` will return the list of all the running containers.
|
||||
|
||||
take the <container_id> (e.g. 4addb0b2d307) and go to its config directory
|
||||
`/var/lib/docker/execdriver/native/4addb0b2d307` and here you can run the nsinit
|
||||
command line utility.
|
||||
|
||||
e.g. `nsinit exec /bin/bash` will start a shell on the already running container.
|
||||
|
||||
# HISTORY
|
||||
Jan 2015, Originally compiled by Shishir Mahajan (shishir dot mahajan at redhat dot com)
|
||||
based on nsinit source material and internal work.
|
|
@ -0,0 +1,132 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/libcontainer"
|
||||
)
|
||||
|
||||
func TestExecIn(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
buffers := newStdBuffers()
|
||||
process := &libcontainer.Process{
|
||||
Args: []string{"sleep", "10"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
pid1, err := container.Start(process)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buffers = newStdBuffers()
|
||||
psPid, err := container.Start(&libcontainer.Process{
|
||||
Args: []string{"ps"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ps, err := os.FindProcess(psPid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ps.Wait(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p, err := os.FindProcess(pid1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := p.Signal(syscall.SIGKILL); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
if _, err := p.Wait(); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
out := buffers.Stdout.String()
|
||||
if !strings.Contains(out, "sleep 10") || !strings.Contains(out, "ps") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInRlimit(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
buffers := newStdBuffers()
|
||||
process := &libcontainer.Process{
|
||||
Args: []string{"sleep", "10"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
pid1, err := container.Start(process)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buffers = newStdBuffers()
|
||||
psPid, err := container.Start(&libcontainer.Process{
|
||||
Args: []string{"/bin/sh", "-c", "ulimit -n"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ps, err := os.FindProcess(psPid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ps.Wait(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p, err := os.FindProcess(pid1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := p.Signal(syscall.SIGKILL); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
if _, err := p.Wait(); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
out := buffers.Stdout.String()
|
||||
if limit := strings.TrimSpace(out); limit != "1024" {
|
||||
t.Fatalf("expected rlimit to be 1024, got %s", limit)
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ func init() {
|
|||
if err != nil {
|
||||
log.Fatalf("unable to initialize for container: %s", err)
|
||||
}
|
||||
factory.StartInitialization(3)
|
||||
os.Exit(1)
|
||||
if err := factory.StartInitialization(3); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,11 +67,27 @@ func copyBusybox(dest string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func newContainer(config *configs.Config) (libcontainer.Container, error) {
|
||||
factory, err := libcontainer.New(".",
|
||||
libcontainer.InitArgs(os.Args[0], "init", "--"),
|
||||
libcontainer.Cgroupfs,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return factory.Create("testCT", config)
|
||||
}
|
||||
|
||||
// runContainer runs the container with the specific config and arguments
|
||||
//
|
||||
// buffers are returned containing the STDOUT and STDERR output for the run
|
||||
// along with the exit code and any go error
|
||||
func runContainer(config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) {
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
defer container.Destroy()
|
||||
buffers = newStdBuffers()
|
||||
process := &libcontainer.Process{
|
||||
Args: args,
|
||||
|
@ -81,32 +97,18 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe
|
|||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
factory, err := libcontainer.New(".", libcontainer.InitArgs(os.Args[0], "init", "--"), libcontainer.Cgroupfs)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
container, err := factory.Create("testCT", config)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
pid, err := container.Start(process)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
ps, err := p.Wait()
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
|
@ -115,6 +117,5 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe
|
|||
} else {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type linuxContainer struct {
|
||||
|
@ -49,7 +49,6 @@ func (c *linuxContainer) State() (*State, error) {
|
|||
}
|
||||
|
||||
func (c *linuxContainer) Processes() ([]int, error) {
|
||||
glog.Info("fetch container processes")
|
||||
pids, err := c.cgroupManager.GetPids()
|
||||
if err != nil {
|
||||
return nil, newSystemError(err)
|
||||
|
@ -58,7 +57,6 @@ func (c *linuxContainer) Processes() ([]int, error) {
|
|||
}
|
||||
|
||||
func (c *linuxContainer) Stats() (*Stats, error) {
|
||||
glog.Info("fetch container stats")
|
||||
var (
|
||||
err error
|
||||
stats = &Stats{}
|
||||
|
@ -94,7 +92,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
|
|||
if err := parent.start(); err != nil {
|
||||
// terminate the process to ensure that it properly is reaped.
|
||||
if err := parent.terminate(); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
return -1, newSystemError(err)
|
||||
}
|
||||
|
@ -207,7 +205,7 @@ func (c *linuxContainer) Destroy() error {
|
|||
}
|
||||
if !c.config.Namespaces.Contains(configs.NEWPID) {
|
||||
if err := killCgroupProcesses(c.cgroupManager); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
}
|
||||
err = c.cgroupManager.Destroy()
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/netlink"
|
||||
"github.com/docker/libcontainer/system"
|
||||
"github.com/docker/libcontainer/user"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type initType string
|
||||
|
@ -235,7 +235,7 @@ func setupRlimits(config *configs.Config) error {
|
|||
func killCgroupProcesses(m cgroups.Manager) error {
|
||||
var procs []*os.Process
|
||||
if err := m.Freeze(configs.Frozen); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
pids, err := m.GetPids()
|
||||
if err != nil {
|
||||
|
@ -246,16 +246,16 @@ func killCgroupProcesses(m cgroups.Manager) error {
|
|||
if p, err := os.FindProcess(pid); err == nil {
|
||||
procs = append(procs, p)
|
||||
if err := p.Kill(); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := m.Freeze(configs.Thawed); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
for _, p := range procs {
|
||||
if _, err := p.Wait(); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -10,9 +10,9 @@ import (
|
|||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/system"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type parentProcess interface {
|
||||
|
@ -82,7 +82,7 @@ func (p *setnsProcess) execSetns() (*os.Process, error) {
|
|||
return nil, newSystemError(err)
|
||||
}
|
||||
if !status.Success() {
|
||||
return nil, newSystemError(&exec.ExitError{status})
|
||||
return nil, newSystemError(&exec.ExitError{ProcessState: status})
|
||||
}
|
||||
var pid *pid
|
||||
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
||||
|
@ -153,7 +153,7 @@ func (p *initProcess) start() error {
|
|||
}
|
||||
if err := parent.start(); err != nil {
|
||||
if err := parent.terminate(); err != nil {
|
||||
glog.Warning(err)
|
||||
log.Warn(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
@ -23,6 +22,7 @@ const (
|
|||
IFLA_VLAN_ID = 1
|
||||
IFLA_NET_NS_FD = 28
|
||||
IFLA_ADDRESS = 1
|
||||
IFLA_BRPORT_MODE = 4
|
||||
SIOC_BRADDBR = 0x89a0
|
||||
SIOC_BRDELBR = 0x89a1
|
||||
SIOC_BRADDIF = 0x89a2
|
||||
|
@ -1253,25 +1253,33 @@ func SetMacAddress(name, addr string) error {
|
|||
}
|
||||
|
||||
func SetHairpinMode(iface *net.Interface, enabled bool) error {
|
||||
sysPath := filepath.Join("/sys/class/net", iface.Name, "brport/hairpin_mode")
|
||||
|
||||
sysFile, err := os.OpenFile(sysPath, os.O_WRONLY, 0)
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sysFile.Close()
|
||||
defer s.Close()
|
||||
req := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
var writeVal []byte
|
||||
msg := newIfInfomsg(syscall.AF_BRIDGE)
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(iface.Index)
|
||||
msg.Change = DEFAULT_CHANGE
|
||||
req.AddData(msg)
|
||||
|
||||
mode := []byte{0}
|
||||
if enabled {
|
||||
writeVal = []byte("1")
|
||||
} else {
|
||||
writeVal = []byte("0")
|
||||
mode[0] = byte(1)
|
||||
}
|
||||
if _, err := sysFile.Write(writeVal); err != nil {
|
||||
|
||||
br := newRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
|
||||
newRtAttrChild(br, IFLA_BRPORT_MODE, mode)
|
||||
req.AddData(br)
|
||||
if err := s.Send(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.HandleAck(req.Seq)
|
||||
}
|
||||
|
||||
func ChangeName(iface *net.Interface, newName string) error {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/utils"
|
||||
|
@ -31,6 +32,7 @@ var execCommand = cli.Command{
|
|||
}
|
||||
|
||||
func execAction(context *cli.Context) {
|
||||
entry := log.WithField("parent", "nsinit")
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
|
@ -42,9 +44,7 @@ func execAction(context *cli.Context) {
|
|||
created := false
|
||||
container, err := factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
if lerr, ok := err.(libcontainer.Error); !ok || lerr.Code() != libcontainer.ContainerNotExists {
|
||||
fatal(err)
|
||||
}
|
||||
entry.Debug("creating container")
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
|
@ -53,6 +53,7 @@ func execAction(context *cli.Context) {
|
|||
if tty.console != nil {
|
||||
config.Console = tty.console.Path()
|
||||
}
|
||||
|
||||
created = true
|
||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
||||
tty.Close()
|
||||
|
@ -72,16 +73,25 @@ func execAction(context *cli.Context) {
|
|||
pid, err := container.Start(process)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
proc, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
status, err := proc.Wait()
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
if created {
|
||||
container.Destroy()
|
||||
}
|
||||
fatal(err)
|
||||
}
|
||||
if created {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer"
|
||||
_ "github.com/docker/libcontainer/nsenter"
|
||||
|
@ -13,6 +13,7 @@ var initCommand = cli.Command{
|
|||
Name: "init",
|
||||
Usage: "runs the init process inside the namespace",
|
||||
Action: func(context *cli.Context) {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
factory, err := libcontainer.New("")
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
|
@ -13,9 +13,9 @@ func main() {
|
|||
app.Version = "2"
|
||||
app.Author = "libcontainer maintainers"
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "nspid"},
|
||||
cli.StringFlag{Name: "console"},
|
||||
cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"},
|
||||
cli.StringFlag{Name: "log-file", Value: "nsinit-debug.log", Usage: "set the log file to output logs to"},
|
||||
cli.BoolFlag{Name: "debug", Usage: "enable debug output in the logs"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
configCommand,
|
||||
|
@ -27,6 +27,19 @@ func main() {
|
|||
unpauseCommand,
|
||||
stateCommand,
|
||||
}
|
||||
app.Before = func(context *cli.Context) error {
|
||||
if context.GlobalBool("debug") {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
if path := context.GlobalString("log-file"); path != "" {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.SetOutput(f)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,194 +1,341 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"apparmor_profile": "docker-default",
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type":"NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
}
|
||||
],
|
||||
"tty": true,
|
||||
"user": "daemon"
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/rootfs/jessie",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "",
|
||||
"hostname": "nsinit",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWPID",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "docker-default",
|
||||
"process_label": "",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": null,
|
||||
"gid_mappings": null,
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,200 +1,354 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"devices": [
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/rootfs/jessie",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "",
|
||||
"hostname": "koye",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWPID",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
},
|
||||
{
|
||||
"address": "172.17.0.101/16",
|
||||
"bridge": "docker0",
|
||||
"veth_prefix": "veth",
|
||||
"gateway": "172.17.42.1",
|
||||
"mtu": 1500,
|
||||
"type": "veth"
|
||||
}
|
||||
],
|
||||
"tty": true
|
||||
"type": "veth",
|
||||
"name": "eth0",
|
||||
"bridge": "docker0",
|
||||
"mac_address": "",
|
||||
"address": "172.17.0.101/16",
|
||||
"gateway": "172.17.42.1",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 1500,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": "vethnsinit"
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "",
|
||||
"process_label": "",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": null,
|
||||
"gid_mappings": null,
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,198 +1,337 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"type": "tmpfs",
|
||||
"destination": "/tmp"
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/rootfs/jessie",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "",
|
||||
"hostname": "nsinit",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
}
|
||||
],
|
||||
"tty": true,
|
||||
"user": "daemon"
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "",
|
||||
"process_label": "",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": null,
|
||||
"gid_mappings": null,
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,199 +1,341 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"type": "tmpfs",
|
||||
"destination": "/tmp"
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
}
|
||||
],
|
||||
"tty": true,
|
||||
"user": "daemon"
|
||||
}
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/home/michael/development/gocode/src/github.com/docker/libcontainer",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "",
|
||||
"hostname": "nsinit",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWPID",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "",
|
||||
"process_label": "",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": null,
|
||||
"gid_mappings": null,
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
},
|
||||
{
|
||||
"address": "172.17.0.101/16",
|
||||
"bridge": "docker0",
|
||||
"veth_prefix": "veth",
|
||||
"mtu": 1500,
|
||||
"type": "veth"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"destination": "0.0.0.0/0",
|
||||
"source": "172.17.0.101",
|
||||
"gateway": "172.17.42.1",
|
||||
"interface_name": "eth0"
|
||||
}
|
||||
],
|
||||
"tty": true
|
||||
}
|
|
@ -1,195 +1,341 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
||||
"mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
}
|
||||
],
|
||||
"tty": true,
|
||||
"user": "daemon"
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/rootfs/jessie",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
||||
"hostname": "nsinit",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWPID",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "",
|
||||
"process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": null,
|
||||
"gid_mappings": null,
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,249 +1,377 @@
|
|||
{
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL"
|
||||
],
|
||||
"cgroups": {
|
||||
"allowed_devices": [
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "m",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"type": 98
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"path": "/dev/console",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"path": "/dev/tty0",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"path": "/dev/tty1",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"name": "docker-koye",
|
||||
"parent": "docker"
|
||||
},
|
||||
"restrict_sys": true,
|
||||
"devices": [
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"path": "/dev/null",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"path": "/dev/zero",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"path": "/dev/full",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 5,
|
||||
"path": "/dev/tty",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"path": "/dev/urandom",
|
||||
"type": 99
|
||||
},
|
||||
{
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"path": "/dev/random",
|
||||
"type": 99
|
||||
}
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"type": "tmpfs",
|
||||
"destination": "/tmp"
|
||||
}
|
||||
],
|
||||
"environment": [
|
||||
"HOME=/",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=koye",
|
||||
"TERM=xterm"
|
||||
],
|
||||
"hostname": "koye",
|
||||
"namespaces": [
|
||||
{"type": "NEWIPC"},
|
||||
{"type": "NEWNET"},
|
||||
{"type": "NEWNS"},
|
||||
{"type": "NEWPID"},
|
||||
{"type": "NEWUTS"},
|
||||
{"type": "NEWUSER"}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"mtu": 1500,
|
||||
"type": "loopback"
|
||||
},
|
||||
{
|
||||
"address": "172.17.0.9/16",
|
||||
"gateway": "172.17.42.1",
|
||||
"bridge": "docker0",
|
||||
"veth_prefix": "veth",
|
||||
"mtu": 1500,
|
||||
"type": "veth"
|
||||
}
|
||||
],
|
||||
"tty": true,
|
||||
"user": "root",
|
||||
"uid_mappings": [
|
||||
{
|
||||
"container_id": 0,
|
||||
"host_id": 1000,
|
||||
"size": 1
|
||||
},
|
||||
{
|
||||
"container_id": 1,
|
||||
"host_id": 1,
|
||||
"size": 999
|
||||
},
|
||||
{
|
||||
"container_id": 1001,
|
||||
"host_id": 1001,
|
||||
"size": 9000
|
||||
}
|
||||
],
|
||||
"gid_mappings": [
|
||||
{
|
||||
"container_id": 0,
|
||||
"host_id": 1000,
|
||||
"size": 1
|
||||
},
|
||||
{
|
||||
"container_id": 1,
|
||||
"host_id": 1,
|
||||
"size": 999
|
||||
},
|
||||
{
|
||||
"container_id": 1001,
|
||||
"host_id": 1001,
|
||||
"size": 9000
|
||||
}
|
||||
],
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 999,
|
||||
"soft": 999
|
||||
}
|
||||
]
|
||||
"no_pivot_root": false,
|
||||
"parent_death_signal": 0,
|
||||
"pivot_dir": "",
|
||||
"rootfs": "/rootfs/jessie",
|
||||
"readonlyfs": false,
|
||||
"mounts": [
|
||||
{
|
||||
"source": "shm",
|
||||
"destination": "/dev/shm",
|
||||
"device": "tmpfs",
|
||||
"flags": 14,
|
||||
"data": "mode=1777,size=65536k",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "mqueue",
|
||||
"destination": "/dev/mqueue",
|
||||
"device": "mqueue",
|
||||
"flags": 14,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
},
|
||||
{
|
||||
"source": "sysfs",
|
||||
"destination": "/sys",
|
||||
"device": "sysfs",
|
||||
"flags": 15,
|
||||
"data": "",
|
||||
"relabel": ""
|
||||
}
|
||||
],
|
||||
"devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/fuse",
|
||||
"major": 10,
|
||||
"minor": 229,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"mount_label": "",
|
||||
"hostname": "nsinit",
|
||||
"console": "",
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "NEWNS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUTS",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWIPC",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWPID",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWNET",
|
||||
"path": ""
|
||||
},
|
||||
{
|
||||
"type": "NEWUSER",
|
||||
"path": ""
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"CHOWN",
|
||||
"DAC_OVERRIDE",
|
||||
"FSETID",
|
||||
"FOWNER",
|
||||
"MKNOD",
|
||||
"NET_RAW",
|
||||
"SETGID",
|
||||
"SETUID",
|
||||
"SETFCAP",
|
||||
"SETPCAP",
|
||||
"NET_BIND_SERVICE",
|
||||
"SYS_CHROOT",
|
||||
"KILL",
|
||||
"AUDIT_WRITE"
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"type": "loopback",
|
||||
"name": "",
|
||||
"bridge": "",
|
||||
"mac_address": "",
|
||||
"address": "127.0.0.1/0",
|
||||
"gateway": "localhost",
|
||||
"ipv6_address": "",
|
||||
"ipv6_gateway": "",
|
||||
"mtu": 0,
|
||||
"txqueuelen": 0,
|
||||
"host_interface_name": ""
|
||||
}
|
||||
],
|
||||
"routes": null,
|
||||
"cgroups": {
|
||||
"name": "libcontainer",
|
||||
"parent": "nsinit",
|
||||
"allow_all_devices": false,
|
||||
"allowed_devices": [
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 98,
|
||||
"path": "",
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"permissions": "m",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/console",
|
||||
"major": 5,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty0",
|
||||
"major": 4,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty1",
|
||||
"major": 4,
|
||||
"minor": 1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 136,
|
||||
"minor": -1,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 5,
|
||||
"minor": 2,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "",
|
||||
"major": 10,
|
||||
"minor": 200,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 0,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/null",
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/zero",
|
||||
"major": 1,
|
||||
"minor": 5,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/full",
|
||||
"major": 1,
|
||||
"minor": 7,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/tty",
|
||||
"major": 5,
|
||||
"minor": 0,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/urandom",
|
||||
"major": 1,
|
||||
"minor": 9,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
},
|
||||
{
|
||||
"type": 99,
|
||||
"path": "/dev/random",
|
||||
"major": 1,
|
||||
"minor": 8,
|
||||
"permissions": "rwm",
|
||||
"file_mode": 438,
|
||||
"uid": 0,
|
||||
"gid": 0
|
||||
}
|
||||
],
|
||||
"memory": 0,
|
||||
"memory_reservation": 0,
|
||||
"memory_swap": 0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"blkio_weight": 0,
|
||||
"freezer": "",
|
||||
"slice": ""
|
||||
},
|
||||
"apparmor_profile": "",
|
||||
"process_label": "",
|
||||
"rlimits": [
|
||||
{
|
||||
"type": 7,
|
||||
"hard": 1024,
|
||||
"soft": 1024
|
||||
}
|
||||
],
|
||||
"additional_groups": null,
|
||||
"uid_mappings": [
|
||||
{
|
||||
"container_id": 0,
|
||||
"host_id": 1000,
|
||||
"size": 1
|
||||
},
|
||||
{
|
||||
"container_id": 1,
|
||||
"host_id": 1,
|
||||
"size": 999
|
||||
},
|
||||
{
|
||||
"container_id": 1001,
|
||||
"host_id": 1001,
|
||||
"size": 2147482647
|
||||
}
|
||||
],
|
||||
"gid_mappings": [
|
||||
{
|
||||
"container_id": 0,
|
||||
"host_id": 1000,
|
||||
"size": 1
|
||||
},
|
||||
{
|
||||
"container_id": 1,
|
||||
"host_id": 1,
|
||||
"size": 999
|
||||
},
|
||||
{
|
||||
"container_id": 1001,
|
||||
"host_id": 1001,
|
||||
"size": 2147482647
|
||||
}
|
||||
],
|
||||
"mask_paths": [
|
||||
"/proc/kcore"
|
||||
],
|
||||
"readonly_paths": [
|
||||
"/proc/sys",
|
||||
"/proc/sysrq-trigger",
|
||||
"/proc/irq",
|
||||
"/proc/bus"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ clone() {
|
|||
# the following lines are in sorted order, FYI
|
||||
clone git github.com/codegangsta/cli 1.1.0
|
||||
clone git github.com/coreos/go-systemd v2
|
||||
clone git github.com/godbus/dbus v1
|
||||
clone git github.com/syndtr/gocapability 3c85049eae
|
||||
clone git github.com/golang/glog 44145f04b68c
|
||||
clone git github.com/godbus/dbus v2
|
||||
clone git github.com/Sirupsen/logrus v0.6.0
|
||||
clone git github.com/syndtr/gocapability 1cf3ac4dc4
|
||||
|
||||
# intentionally not vendoring Docker itself... that'd be a circle :)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
logrus
|
|
@ -0,0 +1,9 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- tip
|
||||
install:
|
||||
- go get github.com/stretchr/testify
|
||||
- go get github.com/stvp/go-udp-testing
|
||||
- go get github.com/tobi/airbrake-go
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Eskildsen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,342 @@
|
|||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||
yet stable (pre 1.0), the core API is unlikely change much but please version
|
||||
control your Logrus to make sure you aren't fetching latest `master` on every
|
||||
build.**
|
||||
|
||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||
plain text):
|
||||
|
||||
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||
|
||||
With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||
|
||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||
|
||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[l2met](http://r.32k.io/l2met-introduction) format:
|
||||
|
||||
```text
|
||||
time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
|
||||
time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
|
||||
time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
|
||||
time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
|
||||
time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
|
||||
```
|
||||
|
||||
#### Example
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
```
|
||||
|
||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
|
||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||
want:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Log as JSON instead of the default ASCII formatter.
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
|
||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||
log.AddHook(&logrus_airbrake.AirbrakeHook{})
|
||||
|
||||
// Output to stderr instead of stdout, could also be a file.
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
// Only log the warning severity or above.
|
||||
log.SetLevel(log.WarnLevel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
||||
```
|
||||
|
||||
For more advanced usage such as logging to multiple locations from the same
|
||||
application, you can also create an instance of the `logrus` Logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Create a new instance of the logger. You can have any number of instances.
|
||||
var log = logrus.New()
|
||||
|
||||
func main() {
|
||||
// The API for setting attributes is a little different than the package level
|
||||
// exported logger. See Godoc.
|
||||
log.Out = os.Stderr
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
}
|
||||
```
|
||||
|
||||
#### Fields
|
||||
|
||||
Logrus encourages careful, structured logging though logging fields instead of
|
||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||
to send event %s to topic %s with key %d")`, you should log the much more
|
||||
discoverable:
|
||||
|
||||
```go
|
||||
log.WithFields(log.Fields{
|
||||
"event": event,
|
||||
"topic": topic,
|
||||
"key": key,
|
||||
}).Fatal("Failed to send event")
|
||||
```
|
||||
|
||||
We've found this API forces you to think about logging in a way that produces
|
||||
much more useful logging messages. We've been in countless situations where just
|
||||
a single added field to a log statement that was already there would've saved us
|
||||
hours. The `WithFields` call is optional.
|
||||
|
||||
In general, with Logrus using any of the `printf`-family functions should be
|
||||
seen as a hint you should add a field, however, you can still use the
|
||||
`printf`-family functions with Logrus.
|
||||
|
||||
#### Hooks
|
||||
|
||||
You can add hooks for logging levels. For example to send errors to an exception
|
||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||
multiple places simultaneously, e.g. syslog.
|
||||
|
||||
```go
|
||||
// Not the real implementation of the Airbrake hook. Just a simple sample.
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.AddHook(new(AirbrakeHook))
|
||||
}
|
||||
|
||||
type AirbrakeHook struct{}
|
||||
|
||||
// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
|
||||
// the fields for the entry. See the Fields section of the README.
|
||||
func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
|
||||
err := airbrake.Notify(entry.Data["error"].(error))
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"source": "airbrake",
|
||||
"endpoint": airbrake.Endpoint,
|
||||
}).Info("Failed to send error to Airbrake")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// `Levels()` returns a slice of `Levels` the hook is fired for.
|
||||
func (hook *AirbrakeHook) Levels() []log.Level {
|
||||
return []log.Level{
|
||||
log.ErrorLevel,
|
||||
log.FatalLevel,
|
||||
log.PanicLevel,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||
"github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.AddHook(new(logrus_airbrake.AirbrakeHook))
|
||||
log.AddHook(logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, ""))
|
||||
}
|
||||
```
|
||||
|
||||
* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
|
||||
Send errors to an exception tracking service compatible with the Airbrake API.
|
||||
Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
|
||||
|
||||
* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
|
||||
Send errors to the Papertrail hosted logging service via UDP.
|
||||
|
||||
* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
|
||||
Send errors to remote syslog server.
|
||||
Uses standard library `log/syslog` behind the scenes.
|
||||
|
||||
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
|
||||
Send errors to a channel in hipchat.
|
||||
|
||||
#### Level logging
|
||||
|
||||
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
||||
|
||||
```go
|
||||
log.Debug("Useful debugging information.")
|
||||
log.Info("Something noteworthy happened!")
|
||||
log.Warn("You should probably take a look at this.")
|
||||
log.Error("Something failed but I'm not quitting.")
|
||||
// Calls os.Exit(1) after logging
|
||||
log.Fatal("Bye.")
|
||||
// Calls panic() after logging
|
||||
log.Panic("I'm bailing.")
|
||||
```
|
||||
|
||||
You can set the logging level on a `Logger`, then it will only log entries with
|
||||
that severity or anything above it:
|
||||
|
||||
```go
|
||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||
log.SetLevel(log.InfoLevel)
|
||||
```
|
||||
|
||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||
environment if your application has that.
|
||||
|
||||
#### Entries
|
||||
|
||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||
automatically added to all logging events:
|
||||
|
||||
1. `time`. The timestamp when the entry was created.
|
||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||
the `AddFields` call. E.g. `Failed to send event.`
|
||||
3. `level`. The logging level. E.g. `info`.
|
||||
|
||||
#### Environments
|
||||
|
||||
Logrus has no notion of environment.
|
||||
|
||||
If you wish for hooks and formatters to only be used in specific environments,
|
||||
you should handle that yourself. For example, if your application has a global
|
||||
variable `Environment`, which is a string representation of the environment you
|
||||
could do:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
init() {
|
||||
// do something here to set environment depending on an environment variable
|
||||
// or command-line flag
|
||||
if Environment == "production" {
|
||||
log.SetFormatter(logrus.JSONFormatter)
|
||||
} else {
|
||||
// The TextFormatter is default, you don't actually have to do this.
|
||||
log.SetFormatter(logrus.TextFormatter)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration is how `logrus` was intended to be used, but JSON in
|
||||
production is mostly only useful if you do log aggregation with tools like
|
||||
Splunk or Logstash.
|
||||
|
||||
#### Formatters
|
||||
|
||||
The built-in logging formatters are:
|
||||
|
||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||
without colors.
|
||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||
field to `true`. To force no colored output even if there is a TTY set the
|
||||
`DisableColors` field to `true`
|
||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||
|
||||
Third party logging formatters:
|
||||
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||
default ones (see Entries section above):
|
||||
|
||||
```go
|
||||
type MyJSONFormatter struct {
|
||||
}
|
||||
|
||||
log.SetFormatter(new(MyJSONFormatter))
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
// Note this doesn't include Time, Level and Message which are available on
|
||||
// the Entry. Consult `godoc` on information about those fields or read the
|
||||
// source of the official loggers.
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Rotation
|
||||
|
||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||
external program (like `logrotated(8)`) that can compress and delete old log
|
||||
entries. It should not be a feature of the application-level logger.
|
||||
|
||||
|
||||
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
|
|
@ -0,0 +1,248 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
||||
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
||||
// passed around as much as you wish to avoid field duplication.
|
||||
type Entry struct {
|
||||
Logger *Logger
|
||||
|
||||
// Contains all the fields set by the user.
|
||||
Data Fields
|
||||
|
||||
// Time at which the log entry was created
|
||||
Time time.Time
|
||||
|
||||
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
|
||||
Level Level
|
||||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
return &Entry{
|
||||
Logger: logger,
|
||||
// Default is three fields, give a little extra room
|
||||
Data: make(Fields, 5),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return reader.String(), err
|
||||
}
|
||||
|
||||
// Add a single field to the Entry.
|
||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||
return entry.WithFields(Fields{key: value})
|
||||
}
|
||||
|
||||
// Add a map of fields to the Entry.
|
||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
data := Fields{}
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
for k, v := range fields {
|
||||
data[k] = v
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data}
|
||||
}
|
||||
|
||||
func (entry *Entry) log(level Level, msg string) {
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
|
||||
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
|
||||
_, err = io.Copy(entry.Logger.Out, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||
// directly here.
|
||||
if level <= PanicLevel {
|
||||
panic(reader.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Debug(args ...interface{}) {
|
||||
if entry.Logger.Level >= DebugLevel {
|
||||
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Print(args ...interface{}) {
|
||||
entry.Info(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Info(args ...interface{}) {
|
||||
if entry.Logger.Level >= InfoLevel {
|
||||
entry.log(InfoLevel, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Warn(args ...interface{}) {
|
||||
if entry.Logger.Level >= WarnLevel {
|
||||
entry.log(WarnLevel, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Error(args ...interface{}) {
|
||||
if entry.Logger.Level >= ErrorLevel {
|
||||
entry.log(ErrorLevel, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
if entry.Logger.Level >= PanicLevel {
|
||||
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||
}
|
||||
panic(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Entry Printf family functions
|
||||
|
||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= DebugLevel {
|
||||
entry.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= InfoLevel {
|
||||
entry.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||
entry.Infof(format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= WarnLevel {
|
||||
entry.Warn(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||
entry.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= ErrorLevel {
|
||||
entry.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= PanicLevel {
|
||||
entry.Panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
// Entry Println family functions
|
||||
|
||||
func (entry *Entry) Debugln(args ...interface{}) {
|
||||
if entry.Logger.Level >= DebugLevel {
|
||||
entry.Debug(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Infoln(args ...interface{}) {
|
||||
if entry.Logger.Level >= InfoLevel {
|
||||
entry.Info(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Println(args ...interface{}) {
|
||||
entry.Infoln(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnln(args ...interface{}) {
|
||||
if entry.Logger.Level >= WarnLevel {
|
||||
entry.Warn(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningln(args ...interface{}) {
|
||||
entry.Warnln(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorln(args ...interface{}) {
|
||||
if entry.Logger.Level >= ErrorLevel {
|
||||
entry.Error(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
if entry.Logger.Level >= PanicLevel {
|
||||
entry.Panic(entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||
// string allocation, we do the simplest thing.
|
||||
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||
msg := fmt.Sprintln(args...)
|
||||
return msg[:len(msg)-1]
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.JSONFormatter)
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/hooks/airbrake"
|
||||
"github.com/tobi/airbrake-go"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
log.Hooks.Add(new(logrus_airbrake.AirbrakeHook))
|
||||
}
|
||||
|
||||
func main() {
|
||||
airbrake.Endpoint = "https://exceptions.whatever.com/notifier_api/v2/notices.xml"
|
||||
airbrake.ApiKey = "whatever"
|
||||
airbrake.Environment = "production"
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// std is the name of the standard logger in stdlib `log`
|
||||
std = New()
|
||||
)
|
||||
|
||||
// SetOutput sets the standard logger output.
|
||||
func SetOutput(out io.Writer) {
|
||||
std.mu.Lock()
|
||||
defer std.mu.Unlock()
|
||||
std.Out = out
|
||||
}
|
||||
|
||||
// SetFormatter sets the standard logger formatter.
|
||||
func SetFormatter(formatter Formatter) {
|
||||
std.mu.Lock()
|
||||
defer std.mu.Unlock()
|
||||
std.Formatter = formatter
|
||||
}
|
||||
|
||||
// SetLevel sets the standard logger level.
|
||||
func SetLevel(level Level) {
|
||||
std.mu.Lock()
|
||||
defer std.mu.Unlock()
|
||||
std.Level = level
|
||||
}
|
||||
|
||||
// AddHook adds a hook to the standard logger hooks.
|
||||
func AddHook(hook Hook) {
|
||||
std.mu.Lock()
|
||||
defer std.mu.Unlock()
|
||||
std.Hooks.Add(hook)
|
||||
}
|
||||
|
||||
// WithField creates an entry from the standard logger and adds a field to
|
||||
// it. If you want multiple fields, use `WithFields`.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithField(key string, value interface{}) *Entry {
|
||||
return std.WithField(key, value)
|
||||
}
|
||||
|
||||
// WithFields creates an entry from the standard logger and adds multiple
|
||||
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||
// once for each field.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithFields(fields Fields) *Entry {
|
||||
return std.WithFields(fields)
|
||||
}
|
||||
|
||||
// Debug logs a message at level Debug on the standard logger.
|
||||
func Debug(args ...interface{}) {
|
||||
std.Debug(args...)
|
||||
}
|
||||
|
||||
// Print logs a message at level Info on the standard logger.
|
||||
func Print(args ...interface{}) {
|
||||
std.Print(args...)
|
||||
}
|
||||
|
||||
// Info logs a message at level Info on the standard logger.
|
||||
func Info(args ...interface{}) {
|
||||
std.Info(args...)
|
||||
}
|
||||
|
||||
// Warn logs a message at level Warn on the standard logger.
|
||||
func Warn(args ...interface{}) {
|
||||
std.Warn(args...)
|
||||
}
|
||||
|
||||
// Warning logs a message at level Warn on the standard logger.
|
||||
func Warning(args ...interface{}) {
|
||||
std.Warning(args...)
|
||||
}
|
||||
|
||||
// Error logs a message at level Error on the standard logger.
|
||||
func Error(args ...interface{}) {
|
||||
std.Error(args...)
|
||||
}
|
||||
|
||||
// Panic logs a message at level Panic on the standard logger.
|
||||
func Panic(args ...interface{}) {
|
||||
std.Panic(args...)
|
||||
}
|
||||
|
||||
// Fatal logs a message at level Fatal on the standard logger.
|
||||
func Fatal(args ...interface{}) {
|
||||
std.Fatal(args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message at level Debug on the standard logger.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
std.Debugf(format, args...)
|
||||
}
|
||||
|
||||
// Printf logs a message at level Info on the standard logger.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
std.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Infof logs a message at level Info on the standard logger.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
std.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf logs a message at level Warn on the standard logger.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
std.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Warningf logs a message at level Warn on the standard logger.
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
std.Warningf(format, args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message at level Error on the standard logger.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
std.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Panicf logs a message at level Panic on the standard logger.
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
std.Panicf(format, args...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message at level Fatal on the standard logger.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
std.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// Debugln logs a message at level Debug on the standard logger.
|
||||
func Debugln(args ...interface{}) {
|
||||
std.Debugln(args...)
|
||||
}
|
||||
|
||||
// Println logs a message at level Info on the standard logger.
|
||||
func Println(args ...interface{}) {
|
||||
std.Println(args...)
|
||||
}
|
||||
|
||||
// Infoln logs a message at level Info on the standard logger.
|
||||
func Infoln(args ...interface{}) {
|
||||
std.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warnln logs a message at level Warn on the standard logger.
|
||||
func Warnln(args ...interface{}) {
|
||||
std.Warnln(args...)
|
||||
}
|
||||
|
||||
// Warningln logs a message at level Warn on the standard logger.
|
||||
func Warningln(args ...interface{}) {
|
||||
std.Warningln(args...)
|
||||
}
|
||||
|
||||
// Errorln logs a message at level Error on the standard logger.
|
||||
func Errorln(args ...interface{}) {
|
||||
std.Errorln(args...)
|
||||
}
|
||||
|
||||
// Panicln logs a message at level Panic on the standard logger.
|
||||
func Panicln(args ...interface{}) {
|
||||
std.Panicln(args...)
|
||||
}
|
||||
|
||||
// Fatalln logs a message at level Fatal on the standard logger.
|
||||
func Fatalln(args ...interface{}) {
|
||||
std.Fatalln(args...)
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package logrus
|
||||
|
||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||
// `Entry`. It exposes all the fields, including the default ones:
|
||||
//
|
||||
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||
// * `entry.Data["time"]`. The timestamp.
|
||||
// * `entry.Data["level"]. The level the entry was logged at.
|
||||
//
|
||||
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||
// logged to `logger.Out`.
|
||||
type Formatter interface {
|
||||
Format(*Entry) ([]byte, error)
|
||||
}
|
||||
|
||||
// This is to not silently overwrite `time`, `msg` and `level` fields when
|
||||
// dumping it. If this code wasn't there doing:
|
||||
//
|
||||
// logrus.WithField("level", 1).Info("hello")
|
||||
//
|
||||
// Would just silently drop the user provided level. Instead with this code
|
||||
// it'll logged as:
|
||||
//
|
||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||
//
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(entry *Entry) {
|
||||
_, ok := entry.Data["time"]
|
||||
if ok {
|
||||
entry.Data["fields.time"] = entry.Data["time"]
|
||||
}
|
||||
|
||||
_, ok = entry.Data["msg"]
|
||||
if ok {
|
||||
entry.Data["fields.msg"] = entry.Data["msg"]
|
||||
}
|
||||
|
||||
_, ok = entry.Data["level"]
|
||||
if ok {
|
||||
entry.Data["fields.level"] = entry.Data["level"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// smallFields is a small size data set for benchmarking
|
||||
var smallFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
}
|
||||
|
||||
// largeFields is a large size data set for benchmarking
|
||||
var largeFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
"five": "six",
|
||||
"seven": "eight",
|
||||
"nine": "ten",
|
||||
"eleven": "twelve",
|
||||
"thirteen": "fourteen",
|
||||
"fifteen": "sixteen",
|
||||
"seventeen": "eighteen",
|
||||
"nineteen": "twenty",
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
"g": "h",
|
||||
"i": "j",
|
||||
"k": "l",
|
||||
"m": "n",
|
||||
"o": "p",
|
||||
"q": "r",
|
||||
"s": "t",
|
||||
"u": "v",
|
||||
"w": "x",
|
||||
"y": "z",
|
||||
"this": "will",
|
||||
"make": "thirty",
|
||||
"entries": "yeah",
|
||||
}
|
||||
|
||||
func BenchmarkSmallTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, largeFields)
|
||||
}
|
||||
|
||||
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
||||
entry := &Entry{
|
||||
Time: time.Time{},
|
||||
Level: InfoLevel,
|
||||
Message: "message",
|
||||
Data: fields,
|
||||
}
|
||||
var d []byte
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
d, err = formatter.Format(entry)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(d)))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *TestHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *TestHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookFires(t *testing.T) {
|
||||
hook := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
|
||||
log.Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ModifyHook struct {
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Fire(entry *Entry) error {
|
||||
entry.Data["wow"] = "whale"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookCanModifyEntry(t *testing.T) {
|
||||
hook := new(ModifyHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanFireMultipleHooks(t *testing.T) {
|
||||
hook1 := new(ModifyHook)
|
||||
hook2 := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook1)
|
||||
log.Hooks.Add(hook2)
|
||||
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
assert.Equal(t, hook2.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ErrorHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Levels() []Level {
|
||||
return []Level{
|
||||
ErrorLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorHookShouldFireOnError(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Error("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package logrus
|
||||
|
||||
// A hook to be fired when logging on the logging levels returned from
|
||||
// `Levels()` on your implementation of the interface. Note that this is not
|
||||
// fired in a goroutine or a channel with workers, you should handle such
|
||||
// functionality yourself if your call is non-blocking and you don't wish for
|
||||
// the logging calls for levels returned from `Levels()` to block.
|
||||
type Hook interface {
|
||||
Levels() []Level
|
||||
Fire(*Entry) error
|
||||
}
|
||||
|
||||
// Internal type for storing the hooks on a logger instance.
|
||||
type levelHooks map[Level][]Hook
|
||||
|
||||
// Add a hook to an instance of logger. This is called with
|
||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||
func (hooks levelHooks) Add(hook Hook) {
|
||||
for _, level := range hook.Levels() {
|
||||
hooks[level] = append(hooks[level], hook)
|
||||
}
|
||||
}
|
||||
|
||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||
// appropriate hooks for a log entry.
|
||||
func (hooks levelHooks) Fire(level Level, entry *Entry) error {
|
||||
for _, hook := range hooks[level] {
|
||||
if err := hook.Fire(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package logrus_airbrake
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/tobi/airbrake-go"
|
||||
)
|
||||
|
||||
// AirbrakeHook to send exceptions to an exception-tracking service compatible
|
||||
// with the Airbrake API. You must set:
|
||||
// * airbrake.Endpoint
|
||||
// * airbrake.ApiKey
|
||||
// * airbrake.Environment (only sends exceptions when set to "production")
|
||||
//
|
||||
// Before using this hook, to send an error. Entries that trigger an Error,
|
||||
// Fatal or Panic should now include an "error" field to send to Airbrake.
|
||||
type AirbrakeHook struct{}
|
||||
|
||||
func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
|
||||
if entry.Data["error"] == nil {
|
||||
entry.Logger.WithFields(logrus.Fields{
|
||||
"source": "airbrake",
|
||||
"endpoint": airbrake.Endpoint,
|
||||
}).Warn("Exceptions sent to Airbrake must have an 'error' key with the error")
|
||||
return nil
|
||||
}
|
||||
|
||||
err, ok := entry.Data["error"].(error)
|
||||
if !ok {
|
||||
entry.Logger.WithFields(logrus.Fields{
|
||||
"source": "airbrake",
|
||||
"endpoint": airbrake.Endpoint,
|
||||
}).Warn("Exceptions sent to Airbrake must have an `error` key of type `error`")
|
||||
return nil
|
||||
}
|
||||
|
||||
airErr := airbrake.Notify(err)
|
||||
if airErr != nil {
|
||||
entry.Logger.WithFields(logrus.Fields{
|
||||
"source": "airbrake",
|
||||
"endpoint": airbrake.Endpoint,
|
||||
"error": airErr,
|
||||
}).Warn("Failed to send error to Airbrake")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *AirbrakeHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
# Papertrail Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
|
||||
|
||||
[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts).
|
||||
|
||||
In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible.
|
||||
|
||||
## Usage
|
||||
|
||||
You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`.
|
||||
|
||||
For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs.
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/hooks/papertrail"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME)
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,54 @@
|
|||
package logrus_papertrail
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
format = "Jan 2 15:04:05"
|
||||
)
|
||||
|
||||
// PapertrailHook to send logs to a logging service compatible with the Papertrail API.
|
||||
type PapertrailHook struct {
|
||||
Host string
|
||||
Port int
|
||||
AppName string
|
||||
UDPConn net.Conn
|
||||
}
|
||||
|
||||
// NewPapertrailHook creates a hook to be added to an instance of logger.
|
||||
func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) {
|
||||
conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port))
|
||||
return &PapertrailHook{host, port, appName, conn}, err
|
||||
}
|
||||
|
||||
// Fire is called when a log event is fired.
|
||||
func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
|
||||
date := time.Now().Format(format)
|
||||
payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message)
|
||||
|
||||
bytesWritten, err := hook.UDPConn.Write([]byte(payload))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels returns the available logging levels.
|
||||
func (hook *PapertrailHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.PanicLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.ErrorLevel,
|
||||
logrus.WarnLevel,
|
||||
logrus.InfoLevel,
|
||||
logrus.DebugLevel,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package logrus_papertrail
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/stvp/go-udp-testing"
|
||||
)
|
||||
|
||||
func TestWritingToUDP(t *testing.T) {
|
||||
port := 16661
|
||||
udp.SetAddr(fmt.Sprintf(":%d", port))
|
||||
|
||||
hook, err := NewPapertrailHook("localhost", port, "test")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to connect to local UDP server.")
|
||||
}
|
||||
|
||||
log := logrus.New()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
udp.ShouldReceive(t, "foo", func() {
|
||||
log.Info("foo")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,59 @@
|
|||
package logrus_syslog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"log/syslog"
|
||||
"os"
|
||||
)
|
||||
|
||||
// SyslogHook to send logs via syslog.
|
||||
type SyslogHook struct {
|
||||
Writer *syslog.Writer
|
||||
SyslogNetwork string
|
||||
SyslogRaddr string
|
||||
}
|
||||
|
||||
// Creates a hook to be added to an instance of logger. This is called with
|
||||
// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
|
||||
// `if err == nil { log.Hooks.Add(hook) }`
|
||||
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
|
||||
w, err := syslog.Dial(network, raddr, priority, tag)
|
||||
return &SyslogHook{w, network, raddr}, err
|
||||
}
|
||||
|
||||
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
|
||||
line, err := entry.String()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
switch entry.Data["level"] {
|
||||
case "panic":
|
||||
return hook.Writer.Crit(line)
|
||||
case "fatal":
|
||||
return hook.Writer.Crit(line)
|
||||
case "error":
|
||||
return hook.Writer.Err(line)
|
||||
case "warn":
|
||||
return hook.Writer.Warning(line)
|
||||
case "info":
|
||||
return hook.Writer.Info(line)
|
||||
case "debug":
|
||||
return hook.Writer.Debug(line)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (hook *SyslogHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.PanicLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.ErrorLevel,
|
||||
logrus.WarnLevel,
|
||||
logrus.InfoLevel,
|
||||
logrus.DebugLevel,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package logrus_syslog
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"log/syslog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLocalhostAddAndPrint(t *testing.T) {
|
||||
log := logrus.New()
|
||||
hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unable to connect to local syslog.")
|
||||
}
|
||||
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
for _, level := range hook.Levels() {
|
||||
if len(log.Hooks[level]) != 1 {
|
||||
t.Errorf("SyslogHook was not added. The length of log.Hooks[%v]: %v", level, len(log.Hooks[level]))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Congratulations!")
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JSONFormatter struct{}
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
prefixFieldClashes(entry)
|
||||
entry.Data["time"] = entry.Time.Format(time.RFC3339)
|
||||
entry.Data["msg"] = entry.Message
|
||||
entry.Data["level"] = entry.Level.String()
|
||||
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||
// file, or leave it default which is `os.Stdout`. You can also set this to
|
||||
// something more adventorous, such as logging to Kafka.
|
||||
Out io.Writer
|
||||
// Hooks for the logger instance. These allow firing events based on logging
|
||||
// levels and log entries. For example, to send errors to an error tracking
|
||||
// service, log to StatsD or dump the core on fatal errors.
|
||||
Hooks levelHooks
|
||||
// All log entries pass through the formatter before logged to Out. The
|
||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||
// own that implements the `Formatter` interface, see the `README` or included
|
||||
// formatters for examples.
|
||||
Formatter Formatter
|
||||
// The logging level the logger should log at. This is typically (and defaults
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||
// instantiate your own:
|
||||
//
|
||||
// var log = &Logger{
|
||||
// Out: os.Stderr,
|
||||
// Formatter: new(JSONFormatter),
|
||||
// Hooks: make(levelHooks),
|
||||
// Level: logrus.Debug,
|
||||
// }
|
||||
//
|
||||
// It's recommended to make this a global instance called `log`.
|
||||
func New() *Logger {
|
||||
return &Logger{
|
||||
Out: os.Stdout,
|
||||
Formatter: new(TextFormatter),
|
||||
Hooks: make(levelHooks),
|
||||
Level: InfoLevel,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// Ff you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
NewEntry(logger).Debug(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
NewEntry(logger).Error(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
NewEntry(logger).Fatal(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
NewEntry(logger).Panic(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
NewEntry(logger).Debugln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
NewEntry(logger).Infoln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
NewEntry(logger).Println(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
NewEntry(logger).Errorln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
NewEntry(logger).Panicln(args...)
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// Level type
|
||||
type Level uint8
|
||||
|
||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||
func (level Level) String() string {
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return "warning"
|
||||
case ErrorLevel:
|
||||
return "error"
|
||||
case FatalLevel:
|
||||
return "fatal"
|
||||
case PanicLevel:
|
||||
return "panic"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
func ParseLevel(lvl string) (Level, error) {
|
||||
switch lvl {
|
||||
case "panic":
|
||||
return PanicLevel, nil
|
||||
case "fatal":
|
||||
return FatalLevel, nil
|
||||
case "error":
|
||||
return ErrorLevel, nil
|
||||
case "warn", "warning":
|
||||
return WarnLevel, nil
|
||||
case "info":
|
||||
return InfoLevel, nil
|
||||
case "debug":
|
||||
return DebugLevel, nil
|
||||
}
|
||||
|
||||
var l Level
|
||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||
}
|
||||
|
||||
// These are the different logging levels. You can set the logging level to log
|
||||
// on your instance of logger, obtained with `logrus.New()`.
|
||||
const (
|
||||
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||
// message passed to Debug, Info, ...
|
||||
PanicLevel Level = iota
|
||||
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
||||
// logging level is set to Panic.
|
||||
FatalLevel
|
||||
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||
// Commonly used for hooks to send errors to an error tracking service.
|
||||
ErrorLevel
|
||||
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||
WarnLevel
|
||||
// InfoLevel level. General operational entries about what's going on inside the
|
||||
// application.
|
||||
InfoLevel
|
||||
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||
DebugLevel
|
||||
)
|
||||
|
||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||
var _ StdLogger = &log.Logger{}
|
||||
|
||||
// StdLogger is what your logrus-enabled library should take, that way
|
||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||
// interface, this is the closest we get, unfortunately.
|
||||
type StdLogger interface {
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
|
||||
Fatal(...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
Fatalln(...interface{})
|
||||
|
||||
Panic(...interface{})
|
||||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
|
||||
var buffer bytes.Buffer
|
||||
var fields Fields
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = new(JSONFormatter)
|
||||
|
||||
log(logger)
|
||||
|
||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assertions(fields)
|
||||
}
|
||||
|
||||
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = &TextFormatter{
|
||||
DisableColors: true,
|
||||
}
|
||||
|
||||
log(logger)
|
||||
|
||||
fields := make(map[string]string)
|
||||
for _, kv := range strings.Split(buffer.String(), " ") {
|
||||
if !strings.Contains(kv, "=") {
|
||||
continue
|
||||
}
|
||||
kvArr := strings.Split(kv, "=")
|
||||
key := strings.TrimSpace(kvArr[0])
|
||||
val, err := strconv.Unquote(kvArr[1])
|
||||
assert.NoError(t, err)
|
||||
fields[key] = val
|
||||
}
|
||||
assertions(fields)
|
||||
}
|
||||
|
||||
func TestPrint(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWarn(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Warn("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["level"], "warning")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln("test", "test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test test")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln("test", 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln(10, 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "10 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Infoln(10, 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "10 10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test", 10)
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Info("test", "test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "testtest")
|
||||
})
|
||||
}
|
||||
|
||||
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
var fields Fields
|
||||
|
||||
logger := New()
|
||||
logger.Out = &buffer
|
||||
logger.Formatter = new(JSONFormatter)
|
||||
|
||||
localLog := logger.WithFields(Fields{
|
||||
"key1": "value1",
|
||||
})
|
||||
|
||||
localLog.WithField("key2", "value2").Info("test")
|
||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "value2", fields["key2"])
|
||||
assert.Equal(t, "value1", fields["key1"])
|
||||
|
||||
buffer = bytes.Buffer{}
|
||||
fields = Fields{}
|
||||
localLog.Info("test")
|
||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, ok := fields["key2"]
|
||||
assert.Equal(t, false, ok)
|
||||
assert.Equal(t, "value1", fields["key1"])
|
||||
}
|
||||
|
||||
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("msg", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("msg", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["msg"], "test")
|
||||
assert.Equal(t, fields["fields.msg"], "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("time", "hello").Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["fields.time"], "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.WithField("level", 1).Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["level"], "info")
|
||||
assert.Equal(t, fields["fields.level"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
|
||||
LogAndAssertText(t, func(log *Logger) {
|
||||
ll := log.WithField("herp", "derp")
|
||||
ll.Info("hello")
|
||||
ll.Info("bye")
|
||||
}, func(fields map[string]string) {
|
||||
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
|
||||
if _, ok := fields[fieldName]; ok {
|
||||
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConvertLevelToString(t *testing.T) {
|
||||
assert.Equal(t, "debug", DebugLevel.String())
|
||||
assert.Equal(t, "info", InfoLevel.String())
|
||||
assert.Equal(t, "warning", WarnLevel.String())
|
||||
assert.Equal(t, "error", ErrorLevel.String())
|
||||
assert.Equal(t, "fatal", FatalLevel.String())
|
||||
assert.Equal(t, "panic", PanicLevel.String())
|
||||
}
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
l, err := ParseLevel("panic")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, PanicLevel, l)
|
||||
|
||||
l, err = ParseLevel("fatal")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, FatalLevel, l)
|
||||
|
||||
l, err = ParseLevel("error")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ErrorLevel, l)
|
||||
|
||||
l, err = ParseLevel("warn")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("warning")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, WarnLevel, l)
|
||||
|
||||
l, err = ParseLevel("info")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, InfoLevel, l)
|
||||
|
||||
l, err = ParseLevel("debug")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, DebugLevel, l)
|
||||
|
||||
l, err = ParseLevel("invalid")
|
||||
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios syscall.Termios
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
|
||||
*/
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]uint8
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
const ioctlReadTermios = syscall.TCGETS
|
||||
|
||||
type Termios syscall.Termios
|
|
@ -0,0 +1,21 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux,!appengine darwin freebsd
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stdout
|
||||
var termios Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Based on ssh/terminal:
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stdout
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
nocolor = 0
|
||||
red = 31
|
||||
green = 32
|
||||
yellow = 33
|
||||
blue = 34
|
||||
)
|
||||
|
||||
var (
|
||||
baseTimestamp time.Time
|
||||
isTerminal bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
baseTimestamp = time.Now()
|
||||
isTerminal = IsTerminal()
|
||||
}
|
||||
|
||||
func miniTS() int {
|
||||
return int(time.Since(baseTimestamp) / time.Second)
|
||||
}
|
||||
|
||||
type TextFormatter struct {
|
||||
// Set to true to bypass checking for a TTY before outputting colors.
|
||||
ForceColors bool
|
||||
DisableColors bool
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
|
||||
var keys []string
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
prefixFieldClashes(entry)
|
||||
|
||||
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
|
||||
|
||||
if isColored {
|
||||
printColored(b, entry, keys)
|
||||
} else {
|
||||
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
|
||||
f.appendKeyValue(b, "level", entry.Level.String())
|
||||
f.appendKeyValue(b, "msg", entry.Message)
|
||||
for _, key := range keys {
|
||||
f.appendKeyValue(b, key, entry.Data[key])
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteByte('\n')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
|
||||
var levelColor int
|
||||
switch entry.Level {
|
||||
case WarnLevel:
|
||||
levelColor = yellow
|
||||
case ErrorLevel, FatalLevel, PanicLevel:
|
||||
levelColor = red
|
||||
default:
|
||||
levelColor = blue
|
||||
}
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
||||
switch value.(type) {
|
||||
case string, error:
|
||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
||||
default:
|
||||
fmt.Fprintf(b, "%v=%v ", key, value)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
# How to Contribute
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.markdown) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
|
@ -0,0 +1,2 @@
|
|||
Brandon Philips <brandon@ifup.org> (@philips)
|
||||
Brian Waldon <brian@waldon.cc> (@bcwaldon)
|
|
@ -175,6 +175,13 @@ func (conn *Conn) BusObject() *Object {
|
|||
// not be called on shared connections.
|
||||
func (conn *Conn) Close() error {
|
||||
conn.outLck.Lock()
|
||||
if conn.closed {
|
||||
// inWorker calls Close on read error, the read error may
|
||||
// be caused by another caller calling Close to shutdown the
|
||||
// dbus connection, a double-close scenario we prevent here.
|
||||
conn.outLck.Unlock()
|
||||
return nil
|
||||
}
|
||||
close(conn.out)
|
||||
conn.closed = true
|
||||
conn.outLck.Unlock()
|
||||
|
@ -317,11 +324,7 @@ func (conn *Conn) inWorker() {
|
|||
}
|
||||
conn.signalsLck.Lock()
|
||||
for _, ch := range conn.signals {
|
||||
// don't block trying to send a signal
|
||||
select {
|
||||
case ch <- signal:
|
||||
default:
|
||||
}
|
||||
ch <- signal
|
||||
}
|
||||
conn.signalsLck.Unlock()
|
||||
case TypeMethodCall:
|
||||
|
@ -508,6 +511,10 @@ type Error struct {
|
|||
Body []interface{}
|
||||
}
|
||||
|
||||
func NewError(name string, body []interface{}) *Error {
|
||||
return &Error{name, body}
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if len(e.Body) >= 1 {
|
||||
s, ok := e.Body[0].(string)
|
||||
|
@ -546,13 +553,14 @@ type transport interface {
|
|||
SendMessage(*Message) error
|
||||
}
|
||||
|
||||
var (
|
||||
transports map[string]func(string) (transport, error) = make(map[string]func(string) (transport, error))
|
||||
)
|
||||
|
||||
func getTransport(address string) (transport, error) {
|
||||
var err error
|
||||
var t transport
|
||||
|
||||
m := map[string]func(string) (transport, error){
|
||||
"unix": newUnixTransport,
|
||||
}
|
||||
addresses := strings.Split(address, ";")
|
||||
for _, v := range addresses {
|
||||
i := strings.IndexRune(v, ':')
|
||||
|
@ -560,7 +568,7 @@ func getTransport(address string) (transport, error) {
|
|||
err = errors.New("dbus: invalid bus address (no transport)")
|
||||
continue
|
||||
}
|
||||
f := m[v[:i]]
|
||||
f := transports[v[:i]]
|
||||
if f == nil {
|
||||
err = errors.New("dbus: invalid bus address (invalid or unsupported transport)")
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@ var IntrospectData = Interface{
|
|||
},
|
||||
}
|
||||
|
||||
// XML document type declaration of the introspection format version 1.0
|
||||
const IntrospectDeclarationString = `
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
`
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Introspectable interface,
|
||||
// as a string.
|
||||
const IntrospectDataString = `
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/xml"
|
||||
"github.com/godbus/dbus"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Introspectable implements org.freedesktop.Introspectable.
|
||||
|
@ -31,7 +32,7 @@ func NewIntrospectable(n *Node) Introspectable {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Introspectable(b)
|
||||
return Introspectable(strings.TrimSpace(IntrospectDeclarationString) + string(b))
|
||||
}
|
||||
|
||||
// Introspect implements org.freedesktop.Introspectable.Introspect.
|
||||
|
@ -50,7 +51,7 @@ func Methods(v interface{}) []Method {
|
|||
}
|
||||
mt := t.Method(i).Type
|
||||
if mt.NumOut() == 0 ||
|
||||
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{"", nil}) {
|
||||
mt.Out(mt.NumOut()-1) != reflect.TypeOf(&dbus.Error{}) {
|
||||
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -22,19 +22,19 @@ const (
|
|||
|
||||
// ErrIfaceNotFound is the error returned to peers who try to access properties
|
||||
// on interfaces that aren't found.
|
||||
var ErrIfaceNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil}
|
||||
var ErrIfaceNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.InterfaceNotFound", nil)
|
||||
|
||||
// ErrPropNotFound is the error returned to peers trying to access properties
|
||||
// that aren't found.
|
||||
var ErrPropNotFound = &dbus.Error{"org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil}
|
||||
var ErrPropNotFound = dbus.NewError("org.freedesktop.DBus.Properties.Error.PropertyNotFound", nil)
|
||||
|
||||
// ErrReadOnly is the error returned to peers trying to set a read-only
|
||||
// property.
|
||||
var ErrReadOnly = &dbus.Error{"org.freedesktop.DBus.Properties.Error.ReadOnly", nil}
|
||||
var ErrReadOnly = dbus.NewError("org.freedesktop.DBus.Properties.Error.ReadOnly", nil)
|
||||
|
||||
// ErrInvalidArg is returned to peers if the type of the property that is being
|
||||
// changed and the argument don't match.
|
||||
var ErrInvalidArg = &dbus.Error{"org.freedesktop.DBus.Properties.Error.InvalidArg", nil}
|
||||
var ErrInvalidArg = dbus.NewError("org.freedesktop.DBus.Properties.Error.InvalidArg", nil)
|
||||
|
||||
// The introspection data for the org.freedesktop.DBus.Properties interface.
|
||||
var IntrospectData = introspect.Interface{
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//+build !windows
|
||||
|
||||
package dbus
|
||||
|
||||
import (
|
||||
|
@ -58,6 +60,10 @@ func newUnixTransport(keys string) (transport, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
transports["unix"] = newUnixTransport
|
||||
}
|
||||
|
||||
func (t *unixTransport) EnableUnixFDs() {
|
||||
t.hasUnixFDs = true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
// The UnixCredentials system call is currently only implemented on Linux
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// https://golang.org/s/go1.4-syscall
|
||||
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||
|
||||
// Local implementation of the UnixCredentials system call for DragonFly BSD
|
||||
|
||||
package dbus
|
||||
|
||||
/*
|
||||
#include <sys/ucred.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go
|
||||
// http://golang.org/src/pkg/syscall/ztypes_dragonfly_amd64.go
|
||||
type Ucred struct {
|
||||
Pid int32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/types_linux.go
|
||||
// http://golang.org/src/pkg/syscall/types_dragonfly.go
|
||||
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/ucred.h
|
||||
const (
|
||||
SizeofUcred = C.sizeof_struct_ucred
|
||||
)
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgAlignOf(salen int) int {
|
||||
// From http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
//salign := sizeofPtr
|
||||
// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
|
||||
// still require 32-bit aligned access to network subsystem.
|
||||
//if darwin64Bit || dragonfly64Bit {
|
||||
// salign = 4
|
||||
//}
|
||||
salign := 4
|
||||
return (salen + salign - 1) & ^(salign - 1)
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_unix.go
|
||||
func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr)))
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// UnixCredentials encodes credentials into a socket control message
|
||||
// for sending to another process. This can be used for
|
||||
// authentication.
|
||||
func UnixCredentials(ucred *Ucred) []byte {
|
||||
b := make([]byte, syscall.CmsgSpace(SizeofUcred))
|
||||
h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
h.Level = syscall.SOL_SOCKET
|
||||
h.Type = syscall.SCM_CREDS
|
||||
h.SetLen(syscall.CmsgLen(SizeofUcred))
|
||||
*((*Ucred)(cmsgData(h))) = *ucred
|
||||
return b
|
||||
}
|
||||
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// ParseUnixCredentials decodes a socket control message that contains
|
||||
// credentials in a Ucred structure. To receive such a message, the
|
||||
// SO_PASSCRED option must be enabled on the socket.
|
||||
func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) {
|
||||
if m.Header.Level != syscall.SOL_SOCKET {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
if m.Header.Type != syscall.SCM_CREDS {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
|
||||
return &ucred, nil
|
||||
}
|
||||
|
||||
func (t *unixTransport) SendNullByte() error {
|
||||
ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())}
|
||||
b := UnixCredentials(ucred)
|
||||
_, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oobn != len(b) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
// +build !darwin
|
||||
// The UnixCredentials system call is currently only implemented on Linux
|
||||
// http://golang.org/src/pkg/syscall/sockcmsg_linux.go
|
||||
// https://golang.org/s/go1.4-syscall
|
||||
// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys
|
||||
|
||||
package dbus
|
||||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -89,18 +90,27 @@ func (v Variant) format() (string, bool) {
|
|||
return "{}", false
|
||||
}
|
||||
unamb := true
|
||||
buf := bytes.NewBuffer([]byte("{"))
|
||||
var buf bytes.Buffer
|
||||
kvs := make([]string, rv.Len())
|
||||
for i, k := range rv.MapKeys() {
|
||||
s, b := MakeVariant(k.Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.Reset()
|
||||
buf.WriteString(s)
|
||||
buf.WriteString(": ")
|
||||
s, b = MakeVariant(rv.MapIndex(k).Interface()).format()
|
||||
unamb = unamb && b
|
||||
buf.WriteString(s)
|
||||
if i != rv.Len()-1 {
|
||||
kvs[i] = buf.String()
|
||||
}
|
||||
buf.Reset()
|
||||
buf.WriteByte('{')
|
||||
sort.Strings(kvs)
|
||||
for i, kv := range kvs {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(kv)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.String(), unamb
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,44 +0,0 @@
|
|||
glog
|
||||
====
|
||||
|
||||
Leveled execution logs for Go.
|
||||
|
||||
This is an efficient pure Go implementation of leveled logs in the
|
||||
manner of the open source C++ package
|
||||
http://code.google.com/p/google-glog
|
||||
|
||||
By binding methods to booleans it is possible to use the log package
|
||||
without paying the expense of evaluating the arguments to the log.
|
||||
Through the -vmodule flag, the package also provides fine-grained
|
||||
control over logging at the file level.
|
||||
|
||||
The comment from glog.go introduces the ideas:
|
||||
|
||||
Package glog implements logging analogous to the Google-internal
|
||||
C++ INFO/ERROR/V setup. It provides functions Info, Warning,
|
||||
Error, Fatal, plus formatting variants such as Infof. It
|
||||
also provides V-style logging controlled by the -v and
|
||||
-vmodule=file=2 flags.
|
||||
|
||||
Basic examples:
|
||||
|
||||
glog.Info("Prepare to repel boarders")
|
||||
|
||||
glog.Fatalf("Initialization failed: %s", err)
|
||||
|
||||
See the documentation for the V function for an explanation
|
||||
of these examples:
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Starting transaction...")
|
||||
}
|
||||
|
||||
glog.V(2).Infoln("Processed", nItems, "elements")
|
||||
|
||||
|
||||
The repository contains an open source version of the log package
|
||||
used inside Google. The master copy of the source lives inside
|
||||
Google, not here. The code in this repo is for export only and is not itself
|
||||
under development. Feature requests will be ignored.
|
||||
|
||||
Send bug reports to golang-nuts@googlegroups.com.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,124 +0,0 @@
|
|||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
||||
//
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// File I/O for logs.
|
||||
|
||||
package glog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MaxSize is the maximum size of a log file in bytes.
|
||||
var MaxSize uint64 = 1024 * 1024 * 1800
|
||||
|
||||
// logDirs lists the candidate directories for new log files.
|
||||
var logDirs []string
|
||||
|
||||
// If non-empty, overrides the choice of directory in which to write logs.
|
||||
// See createLogDirs for the full list of possible destinations.
|
||||
var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
|
||||
|
||||
func createLogDirs() {
|
||||
if *logDir != "" {
|
||||
logDirs = append(logDirs, *logDir)
|
||||
}
|
||||
logDirs = append(logDirs, os.TempDir())
|
||||
}
|
||||
|
||||
var (
|
||||
pid = os.Getpid()
|
||||
program = filepath.Base(os.Args[0])
|
||||
host = "unknownhost"
|
||||
userName = "unknownuser"
|
||||
)
|
||||
|
||||
func init() {
|
||||
h, err := os.Hostname()
|
||||
if err == nil {
|
||||
host = shortHostname(h)
|
||||
}
|
||||
|
||||
current, err := user.Current()
|
||||
if err == nil {
|
||||
userName = current.Username
|
||||
}
|
||||
|
||||
// Sanitize userName since it may contain filepath separators on Windows.
|
||||
userName = strings.Replace(userName, `\`, "_", -1)
|
||||
}
|
||||
|
||||
// shortHostname returns its argument, truncating at the first period.
|
||||
// For instance, given "www.google.com" it returns "www".
|
||||
func shortHostname(hostname string) string {
|
||||
if i := strings.Index(hostname, "."); i >= 0 {
|
||||
return hostname[:i]
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// logName returns a new log file name containing tag, with start time t, and
|
||||
// the name for the symlink for tag.
|
||||
func logName(tag string, t time.Time) (name, link string) {
|
||||
name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
|
||||
program,
|
||||
host,
|
||||
userName,
|
||||
tag,
|
||||
t.Year(),
|
||||
t.Month(),
|
||||
t.Day(),
|
||||
t.Hour(),
|
||||
t.Minute(),
|
||||
t.Second(),
|
||||
pid)
|
||||
return name, program + "." + tag
|
||||
}
|
||||
|
||||
var onceLogDirs sync.Once
|
||||
|
||||
// create creates a new log file and returns the file and its filename, which
|
||||
// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
|
||||
// successfully, create also attempts to update the symlink for that tag, ignoring
|
||||
// errors.
|
||||
func create(tag string, t time.Time) (f *os.File, filename string, err error) {
|
||||
onceLogDirs.Do(createLogDirs)
|
||||
if len(logDirs) == 0 {
|
||||
return nil, "", errors.New("log: no log dirs")
|
||||
}
|
||||
name, link := logName(tag, t)
|
||||
var lastErr error
|
||||
for _, dir := range logDirs {
|
||||
fname := filepath.Join(dir, name)
|
||||
f, err := os.Create(fname)
|
||||
if err == nil {
|
||||
symlink := filepath.Join(dir, link)
|
||||
os.Remove(symlink) // ignore err
|
||||
os.Symlink(name, symlink) // ignore err
|
||||
return f, fname, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
||||
//
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package glog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
stdLog "log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test that shortHostname works as advertised.
|
||||
func TestShortHostname(t *testing.T) {
|
||||
for hostname, expect := range map[string]string{
|
||||
"": "",
|
||||
"host": "host",
|
||||
"host.google.com": "host",
|
||||
} {
|
||||
if got := shortHostname(hostname); expect != got {
|
||||
t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
|
||||
type flushBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (f *flushBuffer) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *flushBuffer) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// swap sets the log writers and returns the old array.
|
||||
func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
old = l.file
|
||||
for i, w := range writers {
|
||||
logging.file[i] = w
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// newBuffers sets the log writers to all new byte buffers and returns the old array.
|
||||
func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
|
||||
return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
|
||||
}
|
||||
|
||||
// contents returns the specified log value as a string.
|
||||
func contents(s severity) string {
|
||||
return logging.file[s].(*flushBuffer).String()
|
||||
}
|
||||
|
||||
// contains reports whether the string is contained in the log.
|
||||
func contains(s severity, str string, t *testing.T) bool {
|
||||
return strings.Contains(contents(s), str)
|
||||
}
|
||||
|
||||
// setFlags configures the logging flags how the test expects them.
|
||||
func setFlags() {
|
||||
logging.toStderr = false
|
||||
}
|
||||
|
||||
// Test that Info works as advertised.
|
||||
func TestInfo(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfoDepth(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
|
||||
f := func() { InfoDepth(1, "depth-test1") }
|
||||
|
||||
// The next three lines must stay together
|
||||
_, _, wantLine, _ := runtime.Caller(0)
|
||||
InfoDepth(0, "depth-test0")
|
||||
f()
|
||||
|
||||
msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n")
|
||||
if len(msgs) != 2 {
|
||||
t.Fatalf("Got %d lines, expected 2", len(msgs))
|
||||
}
|
||||
|
||||
for i, m := range msgs {
|
||||
if !strings.HasPrefix(m, "I") {
|
||||
t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
|
||||
}
|
||||
w := fmt.Sprintf("depth-test%d", i)
|
||||
if !strings.Contains(m, w) {
|
||||
t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
|
||||
}
|
||||
|
||||
// pull out the line number (between : and ])
|
||||
msg := m[strings.LastIndex(m, ":")+1:]
|
||||
x := strings.Index(msg, "]")
|
||||
if x < 0 {
|
||||
t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
|
||||
continue
|
||||
}
|
||||
line, err := strconv.Atoi(msg[:x])
|
||||
if err != nil {
|
||||
t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
|
||||
continue
|
||||
}
|
||||
wantLine++
|
||||
if wantLine != line {
|
||||
t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
CopyStandardLogTo("INFO")
|
||||
}
|
||||
|
||||
// Test that CopyStandardLogTo panics on bad input.
|
||||
func TestCopyStandardLogToPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
|
||||
t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
|
||||
}
|
||||
}()
|
||||
CopyStandardLogTo("LOG")
|
||||
}
|
||||
|
||||
// Test that using the standard log package logs to INFO.
|
||||
func TestStandardLog(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
stdLog.Print("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the header has the correct format.
|
||||
func TestHeader(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
defer func(previous func() time.Time) { timeNow = previous }(timeNow)
|
||||
timeNow = func() time.Time {
|
||||
return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
|
||||
}
|
||||
pid = 1234
|
||||
Info("test")
|
||||
var line int
|
||||
format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n"
|
||||
n, err := fmt.Sscanf(contents(infoLog), format, &line)
|
||||
if n != 1 || err != nil {
|
||||
t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
|
||||
}
|
||||
// Scanf treats multiple spaces as equivalent to a single space,
|
||||
// so check for correct space-padding also.
|
||||
want := fmt.Sprintf(format, line)
|
||||
if contents(infoLog) != want {
|
||||
t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that an Error log goes to Warning and Info.
|
||||
// Even in the Info log, the source character will be E, so the data should
|
||||
// all be identical.
|
||||
func TestError(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Error("test")
|
||||
if !contains(errorLog, "E", t) {
|
||||
t.Errorf("Error has wrong character: %q", contents(errorLog))
|
||||
}
|
||||
if !contains(errorLog, "test", t) {
|
||||
t.Error("Error failed")
|
||||
}
|
||||
str := contents(errorLog)
|
||||
if !contains(warningLog, str, t) {
|
||||
t.Error("Warning failed")
|
||||
}
|
||||
if !contains(infoLog, str, t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a Warning log goes to Info.
|
||||
// Even in the Info log, the source character will be W, so the data should
|
||||
// all be identical.
|
||||
func TestWarning(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Warning("test")
|
||||
if !contains(warningLog, "W", t) {
|
||||
t.Errorf("Warning has wrong character: %q", contents(warningLog))
|
||||
}
|
||||
if !contains(warningLog, "test", t) {
|
||||
t.Error("Warning failed")
|
||||
}
|
||||
str := contents(warningLog)
|
||||
if !contains(infoLog, str, t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a V log goes to Info.
|
||||
func TestV(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.verbosity.Set("2")
|
||||
defer logging.verbosity.Set("0")
|
||||
V(2).Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule enables a log in this file.
|
||||
func TestVmoduleOn(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.vmodule.Set("glog_test=2")
|
||||
defer logging.vmodule.Set("")
|
||||
if !V(1) {
|
||||
t.Error("V not enabled for 1")
|
||||
}
|
||||
if !V(2) {
|
||||
t.Error("V not enabled for 2")
|
||||
}
|
||||
if V(3) {
|
||||
t.Error("V enabled for 3")
|
||||
}
|
||||
V(2).Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule of another file does not enable a log in this file.
|
||||
func TestVmoduleOff(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.vmodule.Set("notthisfile=2")
|
||||
defer logging.vmodule.Set("")
|
||||
for i := 1; i <= 3; i++ {
|
||||
if V(Level(i)) {
|
||||
t.Errorf("V enabled for %d", i)
|
||||
}
|
||||
}
|
||||
V(2).Info("test")
|
||||
if contents(infoLog) != "" {
|
||||
t.Error("V logged incorrectly")
|
||||
}
|
||||
}
|
||||
|
||||
// vGlobs are patterns that match/don't match this file at V=2.
|
||||
var vGlobs = map[string]bool{
|
||||
// Easy to test the numeric match here.
|
||||
"glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
|
||||
"glog_test=2": true,
|
||||
"glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
|
||||
// These all use 2 and check the patterns. All are true.
|
||||
"*=2": true,
|
||||
"?l*=2": true,
|
||||
"????_*=2": true,
|
||||
"??[mno]?_*t=2": true,
|
||||
// These all use 2 and check the patterns. All are false.
|
||||
"*x=2": false,
|
||||
"m*=2": false,
|
||||
"??_*=2": false,
|
||||
"?[abc]?_*t=2": false,
|
||||
}
|
||||
|
||||
// Test that vmodule globbing works as advertised.
|
||||
func testVmoduleGlob(pat string, match bool, t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
defer logging.vmodule.Set("")
|
||||
logging.vmodule.Set(pat)
|
||||
if V(2) != Verbose(match) {
|
||||
t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule globbing works as advertised.
|
||||
func TestVmoduleGlob(t *testing.T) {
|
||||
for glob, match := range vGlobs {
|
||||
testVmoduleGlob(glob, match, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollover(t *testing.T) {
|
||||
setFlags()
|
||||
var err error
|
||||
defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
|
||||
logExitFunc = func(e error) {
|
||||
err = e
|
||||
}
|
||||
defer func(previous uint64) { MaxSize = previous }(MaxSize)
|
||||
MaxSize = 512
|
||||
|
||||
Info("x") // Be sure we have a file.
|
||||
info, ok := logging.file[infoLog].(*syncBuffer)
|
||||
if !ok {
|
||||
t.Fatal("info wasn't created")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("info has initial error: %v", err)
|
||||
}
|
||||
fname0 := info.file.Name()
|
||||
Info(strings.Repeat("x", int(MaxSize))) // force a rollover
|
||||
if err != nil {
|
||||
t.Fatalf("info has error after big write: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the next log file gets a file name with a different
|
||||
// time stamp.
|
||||
//
|
||||
// TODO: determine whether we need to support subsecond log
|
||||
// rotation. C++ does not appear to handle this case (nor does it
|
||||
// handle Daylight Savings Time properly).
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
Info("x") // create a new file
|
||||
if err != nil {
|
||||
t.Fatalf("error after rotation: %v", err)
|
||||
}
|
||||
fname1 := info.file.Name()
|
||||
if fname0 == fname1 {
|
||||
t.Errorf("info.f.Name did not change: %v", fname0)
|
||||
}
|
||||
if info.nbytes >= MaxSize {
|
||||
t.Errorf("file size was not reset: %d", info.nbytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBacktraceAt(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
// The peculiar style of this code simplifies line counting and maintenance of the
|
||||
// tracing block below.
|
||||
var infoLine string
|
||||
setTraceLocation := func(file string, line int, ok bool, delta int) {
|
||||
if !ok {
|
||||
t.Fatal("could not get file:line")
|
||||
}
|
||||
_, file = filepath.Split(file)
|
||||
infoLine = fmt.Sprintf("%s:%d", file, line+delta)
|
||||
err := logging.traceLocation.Set(infoLine)
|
||||
if err != nil {
|
||||
t.Fatal("error setting log_backtrace_at: ", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
// Start of tracing block. These lines know about each other's relative position.
|
||||
_, file, line, ok := runtime.Caller(0)
|
||||
setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
|
||||
Info("we want a stack trace here")
|
||||
}
|
||||
numAppearances := strings.Count(contents(infoLog), infoLine)
|
||||
if numAppearances < 2 {
|
||||
// Need 2 appearances, one in the log header and one in the trace:
|
||||
// log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
|
||||
// ...
|
||||
// github.com/glog/glog_test.go:280 (0x41ba91)
|
||||
// ...
|
||||
// We could be more precise but that would require knowing the details
|
||||
// of the traceback format, which may not be dependable.
|
||||
t.Fatal("got no trace back; log is ", contents(infoLog))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHeader(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf, _, _ := logging.header(infoLog, 0)
|
||||
logging.putBuffer(buf)
|
||||
}
|
||||
}
|
|
@ -60,7 +60,8 @@ type Capabilities interface {
|
|||
Apply(kind CapType) error
|
||||
}
|
||||
|
||||
// NewPid create new initialized Capabilities object for given pid.
|
||||
// NewPid create new initialized Capabilities object for given pid when it
|
||||
// is nonzero, or for the current pid if pid is 0
|
||||
func NewPid(pid int) (Capabilities, error) {
|
||||
return newPid(pid)
|
||||
}
|
||||
|
|
|
@ -351,7 +351,15 @@ func (c *capsV3) Load() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/status", c.hdr.pid))
|
||||
var status_path string
|
||||
|
||||
if c.hdr.pid == 0 {
|
||||
status_path = fmt.Sprintf("/proc/self/status")
|
||||
} else {
|
||||
status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid)
|
||||
}
|
||||
|
||||
f, err := os.Open(status_path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue