mirror of https://github.com/docker/docs.git
				
				
				
			Merge pull request #2489 from jeanlaurent/logrus
Replace custom logging system by logrus
This commit is contained in:
		
						commit
						aca111a373
					
				| 
						 | 
				
			
			@ -55,6 +55,11 @@
 | 
			
		|||
		"github.com/docker/machine/version"
 | 
			
		||||
	],
 | 
			
		||||
	"Deps": [
 | 
			
		||||
		{
 | 
			
		||||
		  "ImportPath": "github.com/Sirupsen/logrus",
 | 
			
		||||
		  "Comment": "v0.8.7-49-gcdaedc6",
 | 
			
		||||
		  "Rev": "cdaedc68f2894175ac2b3221869685602c759e71"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"ImportPath": "github.com/MSOpenTech/azure-sdk-for-go",
 | 
			
		||||
			"Comment": "v1.1-17-g515f3ec",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ func setDebugOutputLevel() {
 | 
			
		|||
	// use -v / --verbose TBQH
 | 
			
		||||
	for _, f := range os.Args {
 | 
			
		||||
		if f == "-D" || f == "--debug" || f == "-debug" {
 | 
			
		||||
			log.IsDebug = true
 | 
			
		||||
			log.SetDebug(true)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ func setDebugOutputLevel() {
 | 
			
		|||
			fmt.Fprintf(os.Stderr, "Error parsing boolean value from MACHINE_DEBUG: %s\n", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		log.IsDebug = showDebug
 | 
			
		||||
		log.SetDebug(showDebug)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ package commands
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/machine/libmachine"
 | 
			
		||||
	"github.com/docker/machine/libmachine/check"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +11,7 @@ import (
 | 
			
		|||
func cmdConfig(c CommandLine, api libmachine.API) error {
 | 
			
		||||
	// Ensure that log messages always go to stderr when this command is
 | 
			
		||||
	// being run (it is intended to be run in a subshell)
 | 
			
		||||
	log.SetOutWriter(os.Stderr)
 | 
			
		||||
	log.RedirectStdOutToStdErr()
 | 
			
		||||
 | 
			
		||||
	if len(c.Args()) != 1 {
 | 
			
		||||
		return ErrExpectedOneMachine
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ func cmdEnv(c CommandLine, api libmachine.API) error {
 | 
			
		|||
 | 
			
		||||
	// Ensure that log messages always go to stderr when this command is
 | 
			
		||||
	// being run (it is intended to be run in a subshell)
 | 
			
		||||
	log.SetOutWriter(os.Stderr)
 | 
			
		||||
	log.RedirectStdOutToStdErr()
 | 
			
		||||
 | 
			
		||||
	if c.Bool("unset") {
 | 
			
		||||
		shellCfg, err = shellCfgUnset(c, api)
 | 
			
		||||
| 
						 | 
				
			
			@ -244,7 +244,7 @@ func detectShell() (string, error) {
 | 
			
		|||
	if shell == "" {
 | 
			
		||||
		// check for windows env and not bash (i.e. msysgit, etc)
 | 
			
		||||
		if runtime.GOOS == "windows" {
 | 
			
		||||
			log.Printf("On Windows, please specify either 'cmd' or 'powershell' with the --shell flag.\n\n")
 | 
			
		||||
			log.Info("On Windows, please specify either 'cmd' or 'powershell' with the --shell flag.\n\n")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return "", ErrUnknownShell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -477,7 +477,7 @@ func (c *GenericClient) Authenticate(d *Driver) error {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debug("Authenticating...", map[string]interface{}{
 | 
			
		||||
		"AuthUrl":    d.AuthUrl,
 | 
			
		||||
		"Insecure":   d.Insecure,
 | 
			
		||||
		"DomainID":   d.DomainID,
 | 
			
		||||
| 
						 | 
				
			
			@ -485,7 +485,7 @@ func (c *GenericClient) Authenticate(d *Driver) error {
 | 
			
		|||
		"Username":   d.Username,
 | 
			
		||||
		"TenantName": d.TenantName,
 | 
			
		||||
		"TenantID":   d.TenantId,
 | 
			
		||||
	}).Debug("Authenticating...")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	opts := gophercloud.AuthOptions{
 | 
			
		||||
		IdentityEndpoint: d.AuthUrl,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -278,7 +278,7 @@ func (d *Driver) GetIP() (string, error) {
 | 
			
		|||
		return d.IPAddress, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Debug("Looking for the IP address...")
 | 
			
		||||
	log.Debug("Looking for the IP address...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
| 
						 | 
				
			
			@ -306,7 +306,7 @@ func (d *Driver) GetIP() (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) GetState() (state.State, error) {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Debug("Get status for OpenStack instance...")
 | 
			
		||||
	log.Debug("Get status for OpenStack instance...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return state.None, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -316,10 +316,10 @@ func (d *Driver) GetState() (state.State, error) {
 | 
			
		|||
		return state.None, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debug("State for OpenStack instance", map[string]string{
 | 
			
		||||
		"MachineId": d.MachineId,
 | 
			
		||||
		"State":     s,
 | 
			
		||||
	}).Debug("State for OpenStack instance")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	switch s {
 | 
			
		||||
	case "ACTIVE":
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +365,7 @@ func (d *Driver) Create() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) Start() error {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Info("Starting OpenStack instance...")
 | 
			
		||||
	log.Debug("Starting OpenStack instance...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -376,7 +376,7 @@ func (d *Driver) Start() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) Stop() error {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Info("Stopping OpenStack instance...")
 | 
			
		||||
	log.Debug("Stopping OpenStack instance...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -388,7 +388,7 @@ func (d *Driver) Stop() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) Remove() error {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Debug("deleting instance...")
 | 
			
		||||
	log.Debug("deleting instance...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	log.Info("Deleting OpenStack instance...")
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +396,7 @@ func (d *Driver) Remove() error {
 | 
			
		|||
	if err := d.client.DeleteInstance(d); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	log.WithField("Name", d.KeyPairName).Debug("deleting key pair...")
 | 
			
		||||
	log.Debug("deleting key pair...", map[string]string{"Name": d.KeyPairName})
 | 
			
		||||
	if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -404,7 +404,7 @@ func (d *Driver) Remove() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) Restart() error {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Info("Restarting OpenStack instance...")
 | 
			
		||||
	log.Info("Restarting OpenStack instance...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -483,10 +483,10 @@ func (d *Driver) resolveIds() error {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		d.NetworkId = networkID
 | 
			
		||||
		log.WithFields(log.Fields{
 | 
			
		||||
		log.Debug("Found network id using its name", map[string]string{
 | 
			
		||||
			"Name": d.NetworkName,
 | 
			
		||||
			"ID":   d.NetworkId,
 | 
			
		||||
		}).Debug("Found network id using its name")
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.FlavorName != "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -504,10 +504,10 @@ func (d *Driver) resolveIds() error {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		d.FlavorId = flavorID
 | 
			
		||||
		log.WithFields(log.Fields{
 | 
			
		||||
		log.Debug("Found flavor id using its name", map[string]string{
 | 
			
		||||
			"Name": d.FlavorName,
 | 
			
		||||
			"ID":   d.FlavorId,
 | 
			
		||||
		}).Debug("Found flavor id using its name")
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.ImageName != "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -525,10 +525,10 @@ func (d *Driver) resolveIds() error {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		d.ImageId = imageID
 | 
			
		||||
		log.WithFields(log.Fields{
 | 
			
		||||
		log.Debug("Found image id using its name", map[string]string{
 | 
			
		||||
			"Name": d.ImageName,
 | 
			
		||||
			"ID":   d.ImageId,
 | 
			
		||||
		}).Debug("Found image id using its name")
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if d.FloatingIpPool != "" && !d.ComputeNetwork {
 | 
			
		||||
| 
						 | 
				
			
			@ -546,10 +546,10 @@ func (d *Driver) resolveIds() error {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		d.FloatingIpPoolId = f
 | 
			
		||||
		log.WithFields(log.Fields{
 | 
			
		||||
		log.Debug("Found floating IP pool id using its name", map[string]string{
 | 
			
		||||
			"Name": d.FloatingIpPool,
 | 
			
		||||
			"ID":   d.FloatingIpPoolId,
 | 
			
		||||
		}).Debug("Found floating IP pool id using its name")
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -577,7 +577,7 @@ func (d *Driver) initNetwork() error {
 | 
			
		|||
 | 
			
		||||
func (d *Driver) createSSHKey() error {
 | 
			
		||||
	sanitizeKeyPairName(&d.KeyPairName)
 | 
			
		||||
	log.WithField("Name", d.KeyPairName).Debug("Creating Key Pair...")
 | 
			
		||||
	log.Debug("Creating Key Pair...", map[string]string{"Name": d.KeyPairName})
 | 
			
		||||
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -596,10 +596,10 @@ func (d *Driver) createSSHKey() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) createMachine() error {
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debug("Creating OpenStack instance...", map[string]string{
 | 
			
		||||
		"FlavorId": d.FlavorId,
 | 
			
		||||
		"ImageId":  d.ImageId,
 | 
			
		||||
	}).Debug("Creating OpenStack instance...")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err := d.initCompute(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -632,17 +632,17 @@ func (d *Driver) assignFloatingIP() error {
 | 
			
		|||
 | 
			
		||||
	var floatingIP *FloatingIP
 | 
			
		||||
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debugf("Looking for an available floating IP", map[string]string{
 | 
			
		||||
		"MachineId": d.MachineId,
 | 
			
		||||
		"Pool":      d.FloatingIpPool,
 | 
			
		||||
	}).Debugf("Looking for an available floating IP")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	for _, ip := range ips {
 | 
			
		||||
		if ip.PortId == "" {
 | 
			
		||||
			log.WithFields(log.Fields{
 | 
			
		||||
			log.Debug("Available floating IP found", map[string]string{
 | 
			
		||||
				"MachineId": d.MachineId,
 | 
			
		||||
				"IP":        ip.Ip,
 | 
			
		||||
			}).Debugf("Available floating IP found")
 | 
			
		||||
			})
 | 
			
		||||
			floatingIP = &ip
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -650,9 +650,9 @@ func (d *Driver) assignFloatingIP() error {
 | 
			
		|||
 | 
			
		||||
	if floatingIP == nil {
 | 
			
		||||
		floatingIP = &FloatingIP{}
 | 
			
		||||
		log.WithField("MachineId", d.MachineId).Debugf("No available floating IP found. Allocating a new one...")
 | 
			
		||||
		log.Debug("No available floating IP found. Allocating a new one...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	} else {
 | 
			
		||||
		log.WithField("MachineId", d.MachineId).Debugf("Assigning floating IP to the instance")
 | 
			
		||||
		log.Debug("Assigning floating IP to the instance", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := d.client.AssignFloatingIP(d, floatingIP); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -663,7 +663,7 @@ func (d *Driver) assignFloatingIP() error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (d *Driver) waitForInstanceActive() error {
 | 
			
		||||
	log.WithField("MachineId", d.MachineId).Debug("Waiting for the OpenStack instance to be ACTIVE...")
 | 
			
		||||
	log.Debug("Waiting for the OpenStack instance to be ACTIVE...", map[string]string{"MachineId": d.MachineId})
 | 
			
		||||
	if err := d.client.WaitForInstanceStatus(d, "ACTIVE"); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -676,10 +676,10 @@ func (d *Driver) lookForIPAddress() error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	d.IPAddress = ip
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debug("IP address found", map[string]string{
 | 
			
		||||
		"IP":        ip,
 | 
			
		||||
		"MachineId": d.MachineId,
 | 
			
		||||
	}).Debug("IP address found")
 | 
			
		||||
	})
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,9 +26,9 @@ func (c *Client) Authenticate(d *openstack.Driver) error {
 | 
			
		|||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
	log.Debug("Authenticating to Rackspace.", map[string]string{
 | 
			
		||||
		"Username": d.Username,
 | 
			
		||||
	}).Debug("Authenticating to Rackspace.")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	apiKey := c.driver.APIKey
 | 
			
		||||
	opts := gophercloud.AuthOptions{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,9 +82,7 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
 | 
			
		|||
 | 
			
		||||
// NewDriver instantiates a Rackspace driver.
 | 
			
		||||
func NewDriver(machineName, storePath string) drivers.Driver {
 | 
			
		||||
	log.WithFields(log.Fields{
 | 
			
		||||
		"machineName": machineName,
 | 
			
		||||
	}).Debug("Instantiating Rackspace driver.")
 | 
			
		||||
	log.Debug("Instantiating Rackspace driver.", map[string]string{"machineName": machineName})
 | 
			
		||||
 | 
			
		||||
	inner := openstack.NewDerivedDriver(machineName, storePath)
 | 
			
		||||
	driver := &Driver{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,11 @@ import (
 | 
			
		|||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/machine/libmachine/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,18 +78,16 @@ func TestLocalBinaryPluginClose(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestExecServer(t *testing.T) {
 | 
			
		||||
	log.IsDebug = true
 | 
			
		||||
	log.SetDebug(true)
 | 
			
		||||
	machineName := "test"
 | 
			
		||||
 | 
			
		||||
	logReader, logWriter := io.Pipe()
 | 
			
		||||
 | 
			
		||||
	log.SetOutWriter(logWriter)
 | 
			
		||||
	log.SetErrWriter(logWriter)
 | 
			
		||||
	log.SetOutput(logWriter)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		log.IsDebug = false
 | 
			
		||||
		log.SetOutWriter(os.Stdout)
 | 
			
		||||
		log.SetErrWriter(os.Stderr)
 | 
			
		||||
		log.SetDebug(false)
 | 
			
		||||
		log.SetOutput(os.Stderr)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	stdoutReader, stdoutWriter := io.Pipe()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ Please use this plugin through the main 'docker-machine' binary.
 | 
			
		|||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.IsDebug = true
 | 
			
		||||
	log.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	rpcd := rpcdriver.NewRPCServerDriver(d)
 | 
			
		||||
	rpc.RegisterName(rpcdriver.RPCServiceNameV0, rpcd)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@ package main
 | 
			
		|||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/machine/drivers/virtualbox"
 | 
			
		||||
	"github.com/docker/machine/libmachine"
 | 
			
		||||
| 
						 | 
				
			
			@ -12,9 +11,7 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	log.IsDebug = true
 | 
			
		||||
	log.SetOutWriter(os.Stdout)
 | 
			
		||||
	log.SetErrWriter(os.Stderr)
 | 
			
		||||
	log.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	client := libmachine.NewClient("/tmp/automatic")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,124 +1,66 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
// Logger - Why the interface?  We may only want to print to STDOUT and STDERR for now,
 | 
			
		||||
// but it won't neccessarily be that way forever.  This interface is intended
 | 
			
		||||
// to provide a "framework" for a variety of different logging types in the
 | 
			
		||||
// future (log to file, log to logstash, etc.) There could be a driver model
 | 
			
		||||
// similar to what is done with OS or machine providers.
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	Debug(...interface{})
 | 
			
		||||
	Debugf(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	Error(...interface{})
 | 
			
		||||
	Errorf(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	Info(...interface{})
 | 
			
		||||
	Infof(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	Fatal(...interface{})
 | 
			
		||||
	Fatalf(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	Print(...interface{})
 | 
			
		||||
	Printf(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	Warn(...interface{})
 | 
			
		||||
	Warnf(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	WithFields(Fields) Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	l = StandardLogger{
 | 
			
		||||
		mu: &sync.Mutex{},
 | 
			
		||||
	}
 | 
			
		||||
	IsDebug = false
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Fields map[string]interface{}
 | 
			
		||||
var logger MachineLogger
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// TODO: Is this really the best approach?  I worry that it will create
 | 
			
		||||
	// implicit behavior which may be problmatic for users of the lib.
 | 
			
		||||
	SetOutWriter(os.Stdout)
 | 
			
		||||
	SetErrWriter(os.Stderr)
 | 
			
		||||
	logger = NewMachineLogger()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetOutWriter(w io.Writer) {
 | 
			
		||||
	l.OutWriter = w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetErrWriter(w io.Writer) {
 | 
			
		||||
	l.ErrWriter = w
 | 
			
		||||
// RedirectStdOutToStdErr prevents any log from corrupting the output
 | 
			
		||||
func RedirectStdOutToStdErr() {
 | 
			
		||||
	logger.RedirectStdOutToStdErr()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debug(args ...interface{}) {
 | 
			
		||||
	l.Debug(args...)
 | 
			
		||||
	logger.Debug(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Debugf(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Debugf(fmtString, args...)
 | 
			
		||||
	logger.Debugf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Error(args ...interface{}) {
 | 
			
		||||
	l.Error(args...)
 | 
			
		||||
	logger.Error(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Errorf(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Errorf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Errorln(args ...interface{}) {
 | 
			
		||||
	l.Errorln(args...)
 | 
			
		||||
	logger.Errorf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Info(args ...interface{}) {
 | 
			
		||||
	l.Info(args...)
 | 
			
		||||
	logger.Info(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Infof(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Infof(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Infoln(args ...interface{}) {
 | 
			
		||||
	l.Infoln(args...)
 | 
			
		||||
	logger.Infof(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fatal(args ...interface{}) {
 | 
			
		||||
	l.Fatal(args...)
 | 
			
		||||
	logger.Fatal(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Fatalf(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Fatalf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Print(args ...interface{}) {
 | 
			
		||||
	l.Print(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Printf(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Printf(fmtString, args...)
 | 
			
		||||
	logger.Fatalf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warn(args ...interface{}) {
 | 
			
		||||
	l.Warn(args...)
 | 
			
		||||
	logger.Warn(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Warnf(fmtString string, args ...interface{}) {
 | 
			
		||||
	l.Warnf(fmtString, args...)
 | 
			
		||||
	logger.Warnf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithField(fieldName string, field interface{}) Logger {
 | 
			
		||||
	return l.WithFields(Fields{
 | 
			
		||||
		fieldName: field,
 | 
			
		||||
	})
 | 
			
		||||
func Logger() interface{} {
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func WithFields(fields Fields) Logger {
 | 
			
		||||
	return l.WithFields(fields)
 | 
			
		||||
func SetDebug(debug bool) {
 | 
			
		||||
	logger.SetDebug(debug)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetOutput(out io.Writer) {
 | 
			
		||||
	logger.SetOutput(out)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,9 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestFormatterIsSet(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestStandardLoggerWithFields(t *testing.T) {
 | 
			
		||||
	logger := StandardLogger{}
 | 
			
		||||
	withFieldsLogger := logger.WithFields(Fields{
 | 
			
		||||
		"foo":  "bar",
 | 
			
		||||
		"spam": "eggs",
 | 
			
		||||
	})
 | 
			
		||||
	withFieldsStandardLogger, ok := withFieldsLogger.(StandardLogger)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		t.Fatal("Type assertion to StandardLogger failed")
 | 
			
		||||
	}
 | 
			
		||||
	expectedOutFields := "\t\t foo=bar spam=eggs"
 | 
			
		||||
	if withFieldsStandardLogger.fieldOut != expectedOutFields {
 | 
			
		||||
		t.Fatalf("Expected %q, got %q", expectedOutFields, withFieldsStandardLogger.fieldOut)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type LogrusMachineLogger struct {
 | 
			
		||||
	logger *logrus.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMachineLogger() MachineLogger {
 | 
			
		||||
	logrusLogger := logrus.New()
 | 
			
		||||
	logrusLogger.Level = logrus.InfoLevel
 | 
			
		||||
	logrusLogger.Formatter = new(MachineFormatter)
 | 
			
		||||
	return LogrusMachineLogger{logrusLogger}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RedirectStdOutToStdErr prevents any log from corrupting the output
 | 
			
		||||
func (ml LogrusMachineLogger) RedirectStdOutToStdErr() {
 | 
			
		||||
	ml.logger.Level = logrus.ErrorLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) SetDebug(debug bool) {
 | 
			
		||||
	if debug {
 | 
			
		||||
		ml.logger.Level = logrus.DebugLevel
 | 
			
		||||
	} else {
 | 
			
		||||
		ml.logger.Level = logrus.InfoLevel
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) SetOutput(out io.Writer) {
 | 
			
		||||
	ml.logger.Out = out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Logger() interface{} {
 | 
			
		||||
	return ml.logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Debug(args ...interface{}) {
 | 
			
		||||
	ml.logger.Debug(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Debugf(fmtString string, args ...interface{}) {
 | 
			
		||||
	ml.logger.Debugf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Error(args ...interface{}) {
 | 
			
		||||
	ml.logger.Error(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Errorf(fmtString string, args ...interface{}) {
 | 
			
		||||
	ml.logger.Errorf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Info(args ...interface{}) {
 | 
			
		||||
	ml.logger.Info(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Infof(fmtString string, args ...interface{}) {
 | 
			
		||||
	ml.logger.Infof(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Fatal(args ...interface{}) {
 | 
			
		||||
	ml.logger.Fatal(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Fatalf(fmtString string, args ...interface{}) {
 | 
			
		||||
	ml.logger.Fatalf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Warn(args ...interface{}) {
 | 
			
		||||
	ml.logger.Warn(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ml LogrusMachineLogger) Warnf(fmtString string, args ...interface{}) {
 | 
			
		||||
	ml.logger.Warnf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDefaultLevelIsInfo(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
	assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.InfoLevel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSetDebugToTrue(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
	testLogger.SetDebug(true)
 | 
			
		||||
	assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.DebugLevel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSetDebugToFalse(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
	testLogger.SetDebug(true)
 | 
			
		||||
	testLogger.SetDebug(false)
 | 
			
		||||
	assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.InfoLevel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSetSilenceOutput(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
	testLogger.RedirectStdOutToStdErr()
 | 
			
		||||
	assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.ErrorLevel)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDebug(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
	testLogger.SetDebug(true)
 | 
			
		||||
 | 
			
		||||
	result := captureOutput(testLogger, func() { testLogger.Debug("debug") })
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, result, "debug")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInfo(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
 | 
			
		||||
	result := captureOutput(testLogger, func() { testLogger.Info("info") })
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, result, "info")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWarn(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
 | 
			
		||||
	result := captureOutput(testLogger, func() { testLogger.Warn("warn") })
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, result, "warn")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestError(t *testing.T) {
 | 
			
		||||
	testLogger := NewMachineLogger()
 | 
			
		||||
 | 
			
		||||
	result := captureOutput(testLogger, func() { testLogger.Error("error") })
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, result, "error")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func captureOutput(testLogger MachineLogger, lambda func()) string {
 | 
			
		||||
	pipeReader, pipeWriter := io.Pipe()
 | 
			
		||||
	scanner := bufio.NewScanner(pipeReader)
 | 
			
		||||
	testLogger.SetOutput(pipeWriter)
 | 
			
		||||
	go lambda()
 | 
			
		||||
	scanner.Scan()
 | 
			
		||||
	return scanner.Text()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MachineFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *MachineFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
 | 
			
		||||
	b.WriteString(entry.Message)
 | 
			
		||||
	b.WriteByte('\n')
 | 
			
		||||
 | 
			
		||||
	return b.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestWrite(t *testing.T) {
 | 
			
		||||
	entry := logrus.NewEntry(logrus.New())
 | 
			
		||||
	entry.Message = "foobar"
 | 
			
		||||
	formatter := MachineFormatter{}
 | 
			
		||||
	bytes, err := formatter.Format(entry)
 | 
			
		||||
	assert.Nil(t, err)
 | 
			
		||||
	assert.Equal(t, string(bytes[:]), "foobar\n")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import "io"
 | 
			
		||||
 | 
			
		||||
type MachineLogger interface {
 | 
			
		||||
	RedirectStdOutToStdErr()
 | 
			
		||||
 | 
			
		||||
	SetDebug(debug bool)
 | 
			
		||||
 | 
			
		||||
	SetOutput(io.Writer)
 | 
			
		||||
 | 
			
		||||
	Debug(args ...interface{})
 | 
			
		||||
	Debugf(fmtString string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Error(args ...interface{})
 | 
			
		||||
	Errorf(fmtString string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Info(args ...interface{})
 | 
			
		||||
	Infof(fmtString string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Fatal(args ...interface{})
 | 
			
		||||
	Fatalf(fmtString string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Warn(args ...interface{})
 | 
			
		||||
	Warnf(fmtString string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Logger() interface{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,144 +0,0 @@
 | 
			
		|||
package log
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StandardLogger struct {
 | 
			
		||||
	// fieldOut is used to do log.WithFields correctly
 | 
			
		||||
	fieldOut  string
 | 
			
		||||
	OutWriter io.Writer
 | 
			
		||||
	ErrWriter io.Writer
 | 
			
		||||
	mu        *sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) log(args ...interface{}) {
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	fmt.Fprint(t.OutWriter, args...)
 | 
			
		||||
	fmt.Fprint(t.OutWriter, t.fieldOut, "\n")
 | 
			
		||||
	t.fieldOut = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) logf(fmtString string, args ...interface{}) {
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	fmt.Fprintf(t.OutWriter, fmtString, args...)
 | 
			
		||||
	fmt.Fprint(t.OutWriter, "\n")
 | 
			
		||||
	t.fieldOut = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) err(args ...interface{}) {
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	fmt.Fprint(t.ErrWriter, args...)
 | 
			
		||||
	fmt.Fprint(t.ErrWriter, t.fieldOut, "\n")
 | 
			
		||||
	t.fieldOut = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) errf(fmtString string, args ...interface{}) {
 | 
			
		||||
	defer t.mu.Unlock()
 | 
			
		||||
	t.mu.Lock()
 | 
			
		||||
	fmt.Fprintf(t.ErrWriter, fmtString, args...)
 | 
			
		||||
	fmt.Fprint(t.ErrWriter, t.fieldOut, "\n")
 | 
			
		||||
	t.fieldOut = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Debug(args ...interface{}) {
 | 
			
		||||
	if IsDebug {
 | 
			
		||||
		t.err(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Debugf(fmtString string, args ...interface{}) {
 | 
			
		||||
	if IsDebug {
 | 
			
		||||
		t.errf(fmtString, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Error(args ...interface{}) {
 | 
			
		||||
	t.err(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Errorf(fmtString string, args ...interface{}) {
 | 
			
		||||
	t.errf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Errorln(args ...interface{}) {
 | 
			
		||||
	t.err(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Info(args ...interface{}) {
 | 
			
		||||
	t.log(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Infof(fmtString string, args ...interface{}) {
 | 
			
		||||
	t.logf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Infoln(args ...interface{}) {
 | 
			
		||||
	t.log(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Fatal(args ...interface{}) {
 | 
			
		||||
	t.err(args...)
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Fatalf(fmtString string, args ...interface{}) {
 | 
			
		||||
	t.errf(fmtString, args...)
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Print(args ...interface{}) {
 | 
			
		||||
	t.log(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Printf(fmtString string, args ...interface{}) {
 | 
			
		||||
	t.logf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Warn(args ...interface{}) {
 | 
			
		||||
	fmt.Print("WARNING >>> ")
 | 
			
		||||
	t.log(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) Warnf(fmtString string, args ...interface{}) {
 | 
			
		||||
	fmt.Print("WARNING >>> ")
 | 
			
		||||
	t.logf(fmtString, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t StandardLogger) WithFields(fields Fields) Logger {
 | 
			
		||||
	// When the user calls WithFields, we make a string which gets appended
 | 
			
		||||
	// to the output of the final [Info|Warn|Error] call for the
 | 
			
		||||
	// descriptive fields.  Because WithFields returns the proper Logger
 | 
			
		||||
	// (with the fieldOut string set correctly), the logrus syntax will
 | 
			
		||||
	// still work.
 | 
			
		||||
	kvpairs := []string{}
 | 
			
		||||
 | 
			
		||||
	// Why the string slice song and dance?  Because Go's map iteration
 | 
			
		||||
	// order is random, we will get inconsistent results if we don't sort
 | 
			
		||||
	// the fields (or their resulting string K/V pairs, like we have here).
 | 
			
		||||
	// Otherwise, we couldn't test this reliably.
 | 
			
		||||
	for k, v := range fields {
 | 
			
		||||
		kvpairs = append(kvpairs, fmt.Sprintf("%s=%v", k, v))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(kvpairs)
 | 
			
		||||
 | 
			
		||||
	// TODO:
 | 
			
		||||
	// 1. Is this thread-safe?
 | 
			
		||||
	// 2. Add more tabs?
 | 
			
		||||
	t.fieldOut = "\t\t"
 | 
			
		||||
 | 
			
		||||
	for _, s := range kvpairs {
 | 
			
		||||
		// TODO: Is %v the correct format string here?
 | 
			
		||||
		t.fieldOut = fmt.Sprintf("%s %s", t.fieldOut, s)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
logrus
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - 1.3
 | 
			
		||||
  - 1.4
 | 
			
		||||
  - tip
 | 
			
		||||
install:
 | 
			
		||||
  - go get -t ./...
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
# 0.9.0 (Unreleased)
 | 
			
		||||
 | 
			
		||||
* logrus/text_formatter: don't emit empty msg
 | 
			
		||||
* logrus/hooks/airbrake: move out of main repository
 | 
			
		||||
* logrus/hooks/sentry: move out of main repository
 | 
			
		||||
* logrus/hooks/papertrail: move out of main repository
 | 
			
		||||
* logrus/hooks/bugsnag: move out of main repository
 | 
			
		||||
 | 
			
		||||
# 0.8.7
 | 
			
		||||
 | 
			
		||||
* logrus/core: fix possible race (#216)
 | 
			
		||||
* logrus/doc: small typo fixes and doc improvements
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# 0.8.6
 | 
			
		||||
 | 
			
		||||
* hooks/raven: allow passing an initialized client
 | 
			
		||||
 | 
			
		||||
# 0.8.5
 | 
			
		||||
 | 
			
		||||
* logrus/core: revert #208
 | 
			
		||||
 | 
			
		||||
# 0.8.4
 | 
			
		||||
 | 
			
		||||
* formatter/text: fix data race (#218)
 | 
			
		||||
 | 
			
		||||
# 0.8.3
 | 
			
		||||
 | 
			
		||||
* logrus/core: fix entry log level (#208)
 | 
			
		||||
* logrus/core: improve performance of text formatter by 40%
 | 
			
		||||
* logrus/core: expose `LevelHooks` type
 | 
			
		||||
* logrus/core: add support for DragonflyBSD and NetBSD
 | 
			
		||||
* formatter/text: print structs more verbosely
 | 
			
		||||
 | 
			
		||||
# 0.8.2
 | 
			
		||||
 | 
			
		||||
* logrus: fix more Fatal family functions
 | 
			
		||||
 | 
			
		||||
# 0.8.1
 | 
			
		||||
 | 
			
		||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
 | 
			
		||||
 | 
			
		||||
# 0.8.0
 | 
			
		||||
 | 
			
		||||
* logrus: defaults to stderr instead of stdout
 | 
			
		||||
* hooks/sentry: add special field for `*http.Request`
 | 
			
		||||
* formatter/text: ignore Windows for colors
 | 
			
		||||
 | 
			
		||||
# 0.7.3
 | 
			
		||||
 | 
			
		||||
* formatter/\*: allow configuration of timestamp layout
 | 
			
		||||
 | 
			
		||||
# 0.7.2
 | 
			
		||||
 | 
			
		||||
* formatter/text: Add configuration option for time format (#158)
 | 
			
		||||
| 
						 | 
				
			
			@ -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,365 @@
 | 
			
		|||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus) [][godoc]
 | 
			
		||||
 | 
			
		||||
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). Logrus itself is completely stable and has been used in
 | 
			
		||||
many large deployments. The core API is unlikely to 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):
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
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(&log.TextFormatter{})` when a TTY is not
 | 
			
		||||
attached, the output is compatible with the
 | 
			
		||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
 | 
			
		||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
 | 
			
		||||
exit status 1
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
  // Log as JSON instead of the default ASCII formatter.
 | 
			
		||||
  log.SetFormatter(&log.JSONFormatter{})
 | 
			
		||||
 | 
			
		||||
  // 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!")
 | 
			
		||||
 | 
			
		||||
  // A common pattern is to re-use fields between logging statements by re-using
 | 
			
		||||
  // the logrus.Entry returned from WithFields()
 | 
			
		||||
  contextLogger := log.WithFields(log.Fields{
 | 
			
		||||
    "common": "this is a common field",
 | 
			
		||||
    "other": "I also should be logged always",
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  contextLogger.Info("I'll be logged with common and other field")
 | 
			
		||||
  contextLogger.Info("Me too")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
 | 
			
		||||
`init`:
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
import (
 | 
			
		||||
  log "github.com/Sirupsen/logrus"
 | 
			
		||||
  "gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
 | 
			
		||||
  logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
 | 
			
		||||
  "log/syslog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 | 
			
		||||
  // 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(airbrake.NewHook(123, "xyz", "production"))
 | 
			
		||||
 | 
			
		||||
  hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
 | 
			
		||||
  if err != nil {
 | 
			
		||||
    log.Error("Unable to connect to local syslog daemon")
 | 
			
		||||
  } else {
 | 
			
		||||
    log.AddHook(hook)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
 | 
			
		||||
 | 
			
		||||
| Hook  | Description |
 | 
			
		||||
| ----- | ----------- |
 | 
			
		||||
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
 | 
			
		||||
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
 | 
			
		||||
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
 | 
			
		||||
| [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. |
 | 
			
		||||
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
 | 
			
		||||
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
 | 
			
		||||
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
 | 
			
		||||
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
 | 
			
		||||
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
 | 
			
		||||
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
 | 
			
		||||
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
 | 
			
		||||
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
 | 
			
		||||
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
 | 
			
		||||
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
 | 
			
		||||
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
 | 
			
		||||
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
 | 
			
		||||
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
 | 
			
		||||
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
 | 
			
		||||
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
 | 
			
		||||
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
 | 
			
		||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
 | 
			
		||||
 | 
			
		||||
#### 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(&log.JSONFormatter{})
 | 
			
		||||
  } else {
 | 
			
		||||
    // The TextFormatter is default, you don't actually have to do this.
 | 
			
		||||
    log.SetFormatter(&log.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.
 | 
			
		||||
* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
 | 
			
		||||
 | 
			
		||||
    ```go
 | 
			
		||||
      logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"})
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
Third party logging formatters:
 | 
			
		||||
 | 
			
		||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
 | 
			
		||||
* [`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 *MyJSONFormatter) 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
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Logger as an `io.Writer`
 | 
			
		||||
 | 
			
		||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
w := logger.Writer()
 | 
			
		||||
defer w.Close()
 | 
			
		||||
 | 
			
		||||
srv := http.Server{
 | 
			
		||||
    // create a stdlib log.Logger that writes to
 | 
			
		||||
    // logrus.Logger.
 | 
			
		||||
    ErrorLog: log.New(w, "", 0),
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Each line written to that writer will be printed the usual way, using formatters
 | 
			
		||||
and hooks. The level for those entries is `info`.
 | 
			
		||||
 | 
			
		||||
#### Rotation
 | 
			
		||||
 | 
			
		||||
Log rotation is not provided with Logrus. Log rotation should be done by an
 | 
			
		||||
external program (like `logrotate(8)`) that can compress and delete old log
 | 
			
		||||
entries. It should not be a feature of the application-level logger.
 | 
			
		||||
 | 
			
		||||
#### Tools
 | 
			
		||||
 | 
			
		||||
| Tool | Description |
 | 
			
		||||
| ---- | ----------- |
 | 
			
		||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
 | 
			
		||||
 | 
			
		||||
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
/*
 | 
			
		||||
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The simplest way to use Logrus is simply the package-level exported logger:
 | 
			
		||||
 | 
			
		||||
  package main
 | 
			
		||||
 | 
			
		||||
  import (
 | 
			
		||||
    log "github.com/Sirupsen/logrus"
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  func main() {
 | 
			
		||||
    log.WithFields(log.Fields{
 | 
			
		||||
      "animal": "walrus",
 | 
			
		||||
      "number": 1,
 | 
			
		||||
      "size":   10,
 | 
			
		||||
    }).Info("A walrus appears")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
Output:
 | 
			
		||||
  time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
 | 
			
		||||
 | 
			
		||||
For a full guide visit https://github.com/Sirupsen/logrus
 | 
			
		||||
*/
 | 
			
		||||
package logrus
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,264 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Defines the key when adding errors using WithError.
 | 
			
		||||
var ErrorKey = "error"
 | 
			
		||||
 | 
			
		||||
// 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 an error as single field (using the key defined in ErrorKey) to the Entry.
 | 
			
		||||
func (entry *Entry) WithError(err error) *Entry {
 | 
			
		||||
	return entry.WithField(ErrorKey, 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}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function is not declared with a pointer value because otherwise
 | 
			
		||||
// race conditions will occur when using multiple goroutines
 | 
			
		||||
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(&entry)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) Warning(args ...interface{}) {
 | 
			
		||||
	entry.Warn(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...))
 | 
			
		||||
	}
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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...))
 | 
			
		||||
	}
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,50 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = logrus.New()
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.Formatter = new(logrus.JSONFormatter)
 | 
			
		||||
	log.Formatter = new(logrus.TextFormatter) // default
 | 
			
		||||
	log.Level = logrus.DebugLevel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := recover()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.WithFields(logrus.Fields{
 | 
			
		||||
				"omg":    true,
 | 
			
		||||
				"err":    err,
 | 
			
		||||
				"number": 100,
 | 
			
		||||
			}).Fatal("The ice breaks!")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"animal": "walrus",
 | 
			
		||||
		"number": 8,
 | 
			
		||||
	}).Debug("Started observing beach")
 | 
			
		||||
 | 
			
		||||
	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{
 | 
			
		||||
		"temperature": -4,
 | 
			
		||||
	}).Debug("Temperature changes")
 | 
			
		||||
 | 
			
		||||
	log.WithFields(logrus.Fields{
 | 
			
		||||
		"animal": "orca",
 | 
			
		||||
		"size":   9009,
 | 
			
		||||
	}).Panic("It's over 9000!")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
	"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = logrus.New()
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log.Formatter = new(logrus.TextFormatter) // default
 | 
			
		||||
	log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,193 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// std is the name of the standard logger in stdlib `log`
 | 
			
		||||
	std = New()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func StandardLogger() *Logger {
 | 
			
		||||
	return std
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLevel returns the standard logger level.
 | 
			
		||||
func GetLevel() Level {
 | 
			
		||||
	std.mu.Lock()
 | 
			
		||||
	defer std.mu.Unlock()
 | 
			
		||||
	return std.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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
 | 
			
		||||
func WithError(err error) *Entry {
 | 
			
		||||
	return std.WithField(ErrorKey, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,48 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
const DefaultTimestampFormat = time.RFC3339
 | 
			
		||||
 | 
			
		||||
// 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(data Fields) {
 | 
			
		||||
	_, ok := data["time"]
 | 
			
		||||
	if ok {
 | 
			
		||||
		data["fields.time"] = data["time"]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, ok = data["msg"]
 | 
			
		||||
	if ok {
 | 
			
		||||
		data["fields.msg"] = data["msg"]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, ok = data["level"]
 | 
			
		||||
	if ok {
 | 
			
		||||
		data["fields.level"] = data["level"]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
package logstash
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/Sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Formatter generates json in logstash format.
 | 
			
		||||
// Logstash site: http://logstash.net/
 | 
			
		||||
type LogstashFormatter struct {
 | 
			
		||||
	Type string // if not empty use for logstash type field.
 | 
			
		||||
 | 
			
		||||
	// TimestampFormat sets the format used for timestamps.
 | 
			
		||||
	TimestampFormat string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 | 
			
		||||
	entry.Data["@version"] = 1
 | 
			
		||||
 | 
			
		||||
	if f.TimestampFormat == "" {
 | 
			
		||||
		f.TimestampFormat = logrus.DefaultTimestampFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat)
 | 
			
		||||
 | 
			
		||||
	// set message field
 | 
			
		||||
	v, ok := entry.Data["message"]
 | 
			
		||||
	if ok {
 | 
			
		||||
		entry.Data["fields.message"] = v
 | 
			
		||||
	}
 | 
			
		||||
	entry.Data["message"] = entry.Message
 | 
			
		||||
 | 
			
		||||
	// set level field
 | 
			
		||||
	v, ok = entry.Data["level"]
 | 
			
		||||
	if ok {
 | 
			
		||||
		entry.Data["fields.level"] = v
 | 
			
		||||
	}
 | 
			
		||||
	entry.Data["level"] = entry.Level.String()
 | 
			
		||||
 | 
			
		||||
	// set type field
 | 
			
		||||
	if f.Type != "" {
 | 
			
		||||
		v, ok = entry.Data["type"]
 | 
			
		||||
		if ok {
 | 
			
		||||
			entry.Data["fields.type"] = v
 | 
			
		||||
		}
 | 
			
		||||
		entry.Data["type"] = f.Type
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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,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,39 @@
 | 
			
		|||
# 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"
 | 
			
		||||
  logrus_syslog "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)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
 | 
			
		||||
 | 
			
		||||
```go
 | 
			
		||||
import (
 | 
			
		||||
  "log/syslog"
 | 
			
		||||
  "github.com/Sirupsen/logrus"
 | 
			
		||||
  logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
  log       := logrus.New()
 | 
			
		||||
  hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
 | 
			
		||||
 | 
			
		||||
  if err == nil {
 | 
			
		||||
    log.Hooks.Add(hook)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
// +build !windows,!nacl,!plan9
 | 
			
		||||
 | 
			
		||||
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.Level {
 | 
			
		||||
	case logrus.PanicLevel:
 | 
			
		||||
		return hook.Writer.Crit(line)
 | 
			
		||||
	case logrus.FatalLevel:
 | 
			
		||||
		return hook.Writer.Crit(line)
 | 
			
		||||
	case logrus.ErrorLevel:
 | 
			
		||||
		return hook.Writer.Err(line)
 | 
			
		||||
	case logrus.WarnLevel:
 | 
			
		||||
		return hook.Writer.Warning(line)
 | 
			
		||||
	case logrus.InfoLevel:
 | 
			
		||||
		return hook.Writer.Info(line)
 | 
			
		||||
	case logrus.DebugLevel:
 | 
			
		||||
		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,41 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type JSONFormatter struct {
 | 
			
		||||
	// TimestampFormat sets the format used for marshaling timestamps.
 | 
			
		||||
	TimestampFormat string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
	data := make(Fields, len(entry.Data)+3)
 | 
			
		||||
	for k, v := range entry.Data {
 | 
			
		||||
		switch v := v.(type) {
 | 
			
		||||
		case error:
 | 
			
		||||
			// Otherwise errors are ignored by `encoding/json`
 | 
			
		||||
			// https://github.com/Sirupsen/logrus/issues/137
 | 
			
		||||
			data[k] = v.Error()
 | 
			
		||||
		default:
 | 
			
		||||
			data[k] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	prefixFieldClashes(data)
 | 
			
		||||
 | 
			
		||||
	timestampFormat := f.TimestampFormat
 | 
			
		||||
	if timestampFormat == "" {
 | 
			
		||||
		timestampFormat = DefaultTimestampFormat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data["time"] = entry.Time.Format(timestampFormat)
 | 
			
		||||
	data["msg"] = entry.Message
 | 
			
		||||
	data["level"] = entry.Level.String()
 | 
			
		||||
 | 
			
		||||
	serialized, err := json.Marshal(data)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return append(serialized, '\n'), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,212 @@
 | 
			
		|||
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.Stderr`. 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.DebugLevel,
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
// It's recommended to make this a global instance called `log`.
 | 
			
		||||
func New() *Logger {
 | 
			
		||||
	return &Logger{
 | 
			
		||||
		Out:       os.Stderr,
 | 
			
		||||
		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.
 | 
			
		||||
// If 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add an error as single field to the log entry.  All it does is call
 | 
			
		||||
// `WithError` for the given `error`.
 | 
			
		||||
func (logger *Logger) WithError(err error) *Entry {
 | 
			
		||||
	return NewEntry(logger).WithError(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		NewEntry(logger).Debugf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Infof(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		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{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warnf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warnf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		NewEntry(logger).Errorf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		NewEntry(logger).Fatalf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		NewEntry(logger).Panicf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debug(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		NewEntry(logger).Debug(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Info(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		NewEntry(logger).Info(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Print(args ...interface{}) {
 | 
			
		||||
	NewEntry(logger).Info(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warn(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warn(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warning(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warn(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Error(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		NewEntry(logger).Error(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatal(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		NewEntry(logger).Fatal(args...)
 | 
			
		||||
	}
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panic(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		NewEntry(logger).Panic(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Debugln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= DebugLevel {
 | 
			
		||||
		NewEntry(logger).Debugln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Infoln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= InfoLevel {
 | 
			
		||||
		NewEntry(logger).Infoln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Println(args ...interface{}) {
 | 
			
		||||
	NewEntry(logger).Println(args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warnln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warnln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Warningln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= WarnLevel {
 | 
			
		||||
		NewEntry(logger).Warnln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Errorln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= ErrorLevel {
 | 
			
		||||
		NewEntry(logger).Errorln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Fatalln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= FatalLevel {
 | 
			
		||||
		NewEntry(logger).Fatalln(args...)
 | 
			
		||||
	}
 | 
			
		||||
	os.Exit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Panicln(args ...interface{}) {
 | 
			
		||||
	if logger.Level >= PanicLevel {
 | 
			
		||||
		NewEntry(logger).Panicln(args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
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 = &Entry{}
 | 
			
		||||
	_ StdLogger = &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,9 @@
 | 
			
		|||
// +build darwin freebsd openbsd netbsd dragonfly
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
const ioctlReadTermios = syscall.TIOCGETA
 | 
			
		||||
 | 
			
		||||
type Termios syscall.Termios
 | 
			
		||||
| 
						 | 
				
			
			@ -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 darwin freebsd openbsd netbsd dragonfly
 | 
			
		||||
 | 
			
		||||
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,15 @@
 | 
			
		|||
// +build solaris
 | 
			
		||||
 | 
			
		||||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsTerminal returns true if the given file descriptor is a terminal.
 | 
			
		||||
func IsTerminal() bool {
 | 
			
		||||
	_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,161 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	nocolor = 0
 | 
			
		||||
	red     = 31
 | 
			
		||||
	green   = 32
 | 
			
		||||
	yellow  = 33
 | 
			
		||||
	blue    = 34
 | 
			
		||||
	gray    = 37
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
	// Force disabling colors.
 | 
			
		||||
	DisableColors bool
 | 
			
		||||
 | 
			
		||||
	// Disable timestamp logging. useful when output is redirected to logging
 | 
			
		||||
	// system that already adds timestamps.
 | 
			
		||||
	DisableTimestamp bool
 | 
			
		||||
 | 
			
		||||
	// Enable logging the full timestamp when a TTY is attached instead of just
 | 
			
		||||
	// the time passed since beginning of execution.
 | 
			
		||||
	FullTimestamp bool
 | 
			
		||||
 | 
			
		||||
	// TimestampFormat to use for display when a full timestamp is printed
 | 
			
		||||
	TimestampFormat string
 | 
			
		||||
 | 
			
		||||
	// The fields are sorted by default for a consistent output. For applications
 | 
			
		||||
	// that log extremely frequently and don't use the JSON formatter this may not
 | 
			
		||||
	// be desired.
 | 
			
		||||
	DisableSorting bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
 | 
			
		||||
	var keys []string = make([]string, 0, len(entry.Data))
 | 
			
		||||
	for k := range entry.Data {
 | 
			
		||||
		keys = append(keys, k)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !f.DisableSorting {
 | 
			
		||||
		sort.Strings(keys)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b := &bytes.Buffer{}
 | 
			
		||||
 | 
			
		||||
	prefixFieldClashes(entry.Data)
 | 
			
		||||
 | 
			
		||||
	isColorTerminal := isTerminal && (runtime.GOOS != "windows")
 | 
			
		||||
	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
 | 
			
		||||
 | 
			
		||||
	timestampFormat := f.TimestampFormat
 | 
			
		||||
	if timestampFormat == "" {
 | 
			
		||||
		timestampFormat = DefaultTimestampFormat
 | 
			
		||||
	}
 | 
			
		||||
	if isColored {
 | 
			
		||||
		f.printColored(b, entry, keys, timestampFormat)
 | 
			
		||||
	} else {
 | 
			
		||||
		if !f.DisableTimestamp {
 | 
			
		||||
			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
 | 
			
		||||
		}
 | 
			
		||||
		f.appendKeyValue(b, "level", entry.Level.String())
 | 
			
		||||
		if entry.Message != "" {
 | 
			
		||||
			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 (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
 | 
			
		||||
	var levelColor int
 | 
			
		||||
	switch entry.Level {
 | 
			
		||||
	case DebugLevel:
 | 
			
		||||
		levelColor = gray
 | 
			
		||||
	case WarnLevel:
 | 
			
		||||
		levelColor = yellow
 | 
			
		||||
	case ErrorLevel, FatalLevel, PanicLevel:
 | 
			
		||||
		levelColor = red
 | 
			
		||||
	default:
 | 
			
		||||
		levelColor = blue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	levelText := strings.ToUpper(entry.Level.String())[0:4]
 | 
			
		||||
 | 
			
		||||
	if !f.FullTimestamp {
 | 
			
		||||
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
 | 
			
		||||
	}
 | 
			
		||||
	for _, k := range keys {
 | 
			
		||||
		v := entry.Data[k]
 | 
			
		||||
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needsQuoting(text string) bool {
 | 
			
		||||
	for _, ch := range text {
 | 
			
		||||
		if !((ch >= 'a' && ch <= 'z') ||
 | 
			
		||||
			(ch >= 'A' && ch <= 'Z') ||
 | 
			
		||||
			(ch >= '0' && ch <= '9') ||
 | 
			
		||||
			ch == '-' || ch == '.') {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
 | 
			
		||||
 | 
			
		||||
	b.WriteString(key)
 | 
			
		||||
	b.WriteByte('=')
 | 
			
		||||
 | 
			
		||||
	switch value := value.(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		if needsQuoting(value) {
 | 
			
		||||
			b.WriteString(value)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(b, "%q", value)
 | 
			
		||||
		}
 | 
			
		||||
	case error:
 | 
			
		||||
		errmsg := value.Error()
 | 
			
		||||
		if needsQuoting(errmsg) {
 | 
			
		||||
			b.WriteString(errmsg)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(b, "%q", value)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Fprint(b, value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
package logrus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"io"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) Writer() *io.PipeWriter {
 | 
			
		||||
	reader, writer := io.Pipe()
 | 
			
		||||
 | 
			
		||||
	go logger.writerScanner(reader)
 | 
			
		||||
	runtime.SetFinalizer(writer, writerFinalizer)
 | 
			
		||||
 | 
			
		||||
	return writer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
 | 
			
		||||
	scanner := bufio.NewScanner(reader)
 | 
			
		||||
	for scanner.Scan() {
 | 
			
		||||
		logger.Print(scanner.Text())
 | 
			
		||||
	}
 | 
			
		||||
	if err := scanner.Err(); err != nil {
 | 
			
		||||
		logger.Errorf("Error while reading from Writer: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	reader.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writerFinalizer(writer *io.PipeWriter) {
 | 
			
		||||
	writer.Close()
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue