<commit_before>/*
	Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pmm

import (
	"bytes"
	"context"
	"fmt"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/docker/cli/templates"
	consul "github.com/hashicorp/consul/api"
	"github.com/percona/kardianos-service"
)

// Service status description.
type ServiceStatus struct {
	Type     string
	Name     string
	Port     string
	Running  bool
	DSN      string
	Options  string
	SSL      string
	Password string
}

// Sort rows of formatted table output (list, check-networks commands).
type sortOutput []ServiceStatus

func (s sortOutput) Len() int {
	return len(s)
}

func (s sortOutput) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortOutput) Less(i, j int) bool {
	if s[i].Port != s[j].Port {
		return s[i].Port < s[j].Port
	}
	if s[i].Name != s[j].Name {
		return s[i].Name < s[j].Name
	}
	if s[i].Type != s[j].Type {
		return s[i].Type < s[j].Type
	}
	return false
}

type List struct {
	Version string
	ServerInfo
	Platform         string
	Err              string
	Services         []ServiceStatus
	ExternalErr      string
	ExternalServices []ExternalMetrics
}

// Table formats *List.Services as table and returns result as string.
func (l *List) Table() string {
	// Print table.
	maxTypeLen := len("SERVICE TYPE")
	maxNameLen := len("NAME")
	maxDSNlen := len("DATA SOURCE")
	maxOptsLen := len("OPTIONS")
	for _, in := range l.Services {
		if len(in.Type) > maxTypeLen {
			maxTypeLen = len(in.Type)
		}
		if len(in.Name) > maxNameLen {
			maxNameLen = len(in.Name)
		}
		if len(in.DSN) > maxDSNlen {
			maxDSNlen = len(in.DSN)
		}
		if len(in.Options) > maxOptsLen {
			maxOptsLen = len(in.Options)
		}
	}
	maxTypeLen++
	maxNameLen++
	maxDSNlen++
	maxOptsLen++
	maxStatusLen := 8

	out := ""

	fmtPattern := "%%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds\n"
	linefmt := fmt.Sprintf(fmtPattern, maxTypeLen, maxNameLen, 11, maxStatusLen, maxDSNlen, maxOptsLen)

	out = out + fmt.Sprintf(linefmt, strings.Repeat("-", maxTypeLen), strings.Repeat("-", maxNameLen), strings.Repeat("-", 11),
		strings.Repeat("-", maxStatusLen), strings.Repeat("-", maxDSNlen), strings.Repeat("-", maxOptsLen))
	out = out + fmt.Sprintf(linefmt, "SERVICE TYPE", "NAME", "LOCAL PORT", "RUNNING", "DATA SOURCE", "OPTIONS")
	out = out + fmt.Sprintf(linefmt, strings.Repeat("-", maxTypeLen), strings.Repeat("-", maxNameLen), strings.Repeat("-", 11),
		strings.Repeat("-", maxStatusLen), strings.Repeat("-", maxDSNlen), strings.Repeat("-", maxOptsLen))

	maxStatusLen += 11
	linefmt = fmt.Sprintf(fmtPattern, maxTypeLen, maxNameLen, 11, maxStatusLen, maxDSNlen, maxOptsLen)
	for _, i := range l.Services {
		out = out + fmt.Sprintf(linefmt, i.Type, i.Name, i.Port, colorStatus("YES", "NO", i.Running), i.DSN, i.Options)
	}

	return out
}

// ExternalTable formats *List.ExternalServices as table and returns result as string.
func (l *List) ExternalTable() string {
	var buf bytes.Buffer
	w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Job name\tScrape interval\tScrape timeout\tMetrics path\tScheme\tTarget\tLabels\tHealth")
	for _, ext := range l.ExternalServices {
		for _, t := range ext.Targets {
			labels := make([]string, len(t.Labels))
			for i, p := range t.Labels {
				labels[i] = fmt.Sprintf("%s=%q", p.Name, p.Value)
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
				ext.JobName, ext.ScrapeInterval, ext.ScrapeTimeout, ext.MetricsPath, ext.Scheme,
				t.Target, strings.Join(labels, ", "), t.Health,
			)
		}
	}
	w.Flush()
	return buf.String()
}

// Format formats *List with provided format template and returns result as string.
func (l *List) Format(format string) string {
	b := &bytes.Buffer{}
	w := tabwriter.NewWriter(b, 8, 8, 8, ' ', 0)

	if format == "" {
		format = DefaultListTemplate
	}

	tmpl, err := templates.Parse(format)
	if err != nil {
		return err.Error()
	}
	tmpl, err = tmpl.Parse(ServerInfoTemplate)
	if err != nil {
		return err.Error()
	}
	if err := tmpl.Execute(w, l); err != nil {
		return err.Error()
	}

	w.Flush()

	return b.String()
}

// each non-empty field value must end with newline
const (
	DefaultListTemplate = `pmm-admin {{.Version}}

{{template "ServerInfo" .ServerInfo}}
{{printf "%-15s | %s" "Service Manager" .Platform}}
{{if .Err}}
{{.Err}}{{end}}{{if .Services}}
{{.Table}}{{end}}{{if .ExternalErr}}
{{.ExternalErr}}{{end}}{{if .ExternalServices}}
{{.ExternalTable}}{{end}}`
)

// List prints to stdout all services from Consul.
func (a *Admin) List() error {
	l := &List{
		Version:    Version,
		Platform:   service.Platform(),
		ServerInfo: a.serverInfo(),
	}

	defer func() {
		fmt.Print(l.Format(a.Format))
	}()

	var err error
	l.ExternalServices, err = a.ListExternalMetrics(context.TODO())
	if err != nil {
		l.ExternalErr = err.Error() + "\n"
	}

	node, _, err := a.consulAPI.Catalog().Node(a.Config.ClientName, nil)
	if err != nil || node == nil {
		l.Err = fmt.Sprintf("%s '%s'.\n", noMonitoring, a.Config.ClientName)
		return nil
	}

	if len(node.Services) == 0 {
		l.Err = fmt.Sprint("No services under monitoring.\n")
		return nil
	}

	// Get service data
	svcTable := a.getSVCTable(node)
	sort.Sort(sortOutput(svcTable))
	l.Services = svcTable

	return nil
}

func (a *Admin) getSVCTable(node *consul.CatalogNode) []ServiceStatus {
	// Parse all services except mysql:queries.
	var queryServices []*consul.AgentService
	var svcTable []ServiceStatus
	for _, svc := range node.Services {
		// When server hostname == client name, we have to exclude consul.
		if svc.Service == "consul" {
			continue
		}
		switch svc.Service {
		case "mysql:queries", "mongodb:queries":
			queryServices = append(queryServices, svc)
			continue
		}

		status := getServiceStatus(fmt.Sprintf("pmm-%s-%d", strings.Replace(svc.Service, ":", "-", 1), svc.Port))

		opts := []string{}
		name := "-"
		dsn := "-"
		// Get values for service from Consul KV.
		prefix := fmt.Sprintf("%s/%s/", a.Config.ClientName, svc.ID)
		if data, _, err := a.consulAPI.KV().List(prefix, nil); err == nil {
			for _, kvp := range data {
				key := kvp.Key[len(prefix):]
				switch key {
				case "dsn":
					dsn = string(kvp.Value)
				default:
					opts = append(opts, fmt.Sprintf("%s=%s", key, kvp.Value))
				}
			}
		}
		// Parse Consul service tags.
		for _, tag := range svc.Tags {
			if strings.HasPrefix(tag, "alias_") {
				name = tag[6:]
				continue
			}
			if tag == "scheme_https" {
				continue
			}
			tag := strings.Replace(tag, "_", "=", 1)
			opts = append(opts, tag)
		}

		row := ServiceStatus{
			Type:    svc.Service,
			Name:    name,
			Port:    fmt.Sprintf("%d", svc.Port),
			Running: status,
			DSN:     dsn,
			Options: strings.Join(opts, ", "),
		}
		svcTable = append(svcTable, row)
	}

	// Parse queries service.
	for _, queryService := range queryServices {
		status := getServiceStatus(fmt.Sprintf("pmm-%s-%d", strings.Replace(queryService.Service, ":", "-", 1), queryService.Port))

		// Get names from Consul tags.
		names := []string{}
		for _, tag := range queryService.Tags {
			if strings.HasPrefix(tag, "alias_") {
				names = append(names, tag[6:])
			}
		}

		for _, name := range names {
			dsn := "-"
			opts := []string{}
			// Get values for service from Consul KV.
			prefix := fmt.Sprintf("%s/%s/%s/", a.Config.ClientName, queryService.ID, name)
			if data, _, err := a.consulAPI.KV().List(prefix, nil); err == nil {
				for _, kvp := range data {
					key := kvp.Key[len(prefix):]
					switch key {
					case "dsn":
						dsn = string(kvp.Value)
					case "qan_mysql_uuid", "qan_mongodb_uuid":
						f := fmt.Sprintf("%s/config/qan-%s.conf", AgentBaseDir, kvp.Value)
						config, err := getProtoQAN(f)
						if err != nil {
							opts = append(opts, err.Error())
						}
						opts = append(opts, getQueriesOptions(config)...)
						if key == "qan_mysql_uuid" {
							opts = append(opts, getMySQLQueriesOptions(config)...)
						}
					}
				}
			}
			row := ServiceStatus{
				Type:    queryService.Service,
				Name:    name,
				Port:    "-",
				Running: status,
				DSN:     dsn,
				Options: strings.Join(opts, ", "),
			}
			svcTable = append(svcTable, row)
		}
	}

	return svcTable
}
<commit_msg>Fix rare bug.<commit_after>/*
	Copyright (c) 2016, Percona LLC and/or its affiliates. All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package pmm

import (
	"bytes"
	"context"
	"fmt"
	"sort"
	"strings"
	"text/tabwriter"

	"github.com/docker/cli/templates"
	consul "github.com/hashicorp/consul/api"
	"github.com/percona/kardianos-service"
)

// Service status description.
type ServiceStatus struct {
	Type     string
	Name     string
	Port     string
	Running  bool
	DSN      string
	Options  string
	SSL      string
	Password string
}

// Sort rows of formatted table output (list, check-networks commands).
type sortOutput []ServiceStatus

func (s sortOutput) Len() int {
	return len(s)
}

func (s sortOutput) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s sortOutput) Less(i, j int) bool {
	if s[i].Port != s[j].Port {
		return s[i].Port < s[j].Port
	}
	if s[i].Name != s[j].Name {
		return s[i].Name < s[j].Name
	}
	if s[i].Type != s[j].Type {
		return s[i].Type < s[j].Type
	}
	return false
}

type List struct {
	Version string
	ServerInfo
	Platform         string
	Err              string
	Services         []ServiceStatus
	ExternalErr      string
	ExternalServices []ExternalMetrics
}

// Table formats *List.Services as table and returns result as string.
func (l *List) Table() string {
	// Print table.
	maxTypeLen := len("SERVICE TYPE")
	maxNameLen := len("NAME")
	maxDSNlen := len("DATA SOURCE")
	maxOptsLen := len("OPTIONS")
	for _, in := range l.Services {
		if len(in.Type) > maxTypeLen {
			maxTypeLen = len(in.Type)
		}
		if len(in.Name) > maxNameLen {
			maxNameLen = len(in.Name)
		}
		if len(in.DSN) > maxDSNlen {
			maxDSNlen = len(in.DSN)
		}
		if len(in.Options) > maxOptsLen {
			maxOptsLen = len(in.Options)
		}
	}
	maxTypeLen++
	maxNameLen++
	maxDSNlen++
	maxOptsLen++
	maxStatusLen := 8

	out := ""

	fmtPattern := "%%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds\n"
	linefmt := fmt.Sprintf(fmtPattern, maxTypeLen, maxNameLen, 11, maxStatusLen, maxDSNlen, maxOptsLen)

	out = out + fmt.Sprintf(linefmt, strings.Repeat("-", maxTypeLen), strings.Repeat("-", maxNameLen), strings.Repeat("-", 11),
		strings.Repeat("-", maxStatusLen), strings.Repeat("-", maxDSNlen), strings.Repeat("-", maxOptsLen))
	out = out + fmt.Sprintf(linefmt, "SERVICE TYPE", "NAME", "LOCAL PORT", "RUNNING", "DATA SOURCE", "OPTIONS")
	out = out + fmt.Sprintf(linefmt, strings.Repeat("-", maxTypeLen), strings.Repeat("-", maxNameLen), strings.Repeat("-", 11),
		strings.Repeat("-", maxStatusLen), strings.Repeat("-", maxDSNlen), strings.Repeat("-", maxOptsLen))

	maxStatusLen += 11
	linefmt = fmt.Sprintf(fmtPattern, maxTypeLen, maxNameLen, 11, maxStatusLen, maxDSNlen, maxOptsLen)
	for _, i := range l.Services {
		out = out + fmt.Sprintf(linefmt, i.Type, i.Name, i.Port, colorStatus("YES", "NO", i.Running), i.DSN, i.Options)
	}

	return out
}

// ExternalTable formats *List.ExternalServices as table and returns result as string.
func (l *List) ExternalTable() string {
	var buf bytes.Buffer
	w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
	fmt.Fprintln(w, "Job name\tScrape interval\tScrape timeout\tMetrics path\tScheme\tTarget\tLabels\tHealth")
	for _, ext := range l.ExternalServices {
		for _, t := range ext.Targets {
			labels := make([]string, len(t.Labels))
			for i, p := range t.Labels {
				labels[i] = fmt.Sprintf("%s=%q", p.Name, p.Value)
			}
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
				ext.JobName, ext.ScrapeInterval, ext.ScrapeTimeout, ext.MetricsPath, ext.Scheme,
				t.Target, strings.Join(labels, ", "), t.Health,
			)
		}
	}
	w.Flush()
	return buf.String()
}

// Format formats *List with provided format template and returns result as string.
func (l *List) Format(format string) string {
	b := &bytes.Buffer{}
	w := tabwriter.NewWriter(b, 8, 8, 8, ' ', 0)

	if format == "" {
		format = DefaultListTemplate
	}

	tmpl, err := templates.Parse(format)
	if err != nil {
		return err.Error()
	}
	tmpl, err = tmpl.Parse(ServerInfoTemplate)
	if err != nil {
		return err.Error()
	}
	if err := tmpl.Execute(w, l); err != nil {
		return err.Error()
	}

	w.Flush()

	return b.String()
}

// each non-empty field value must end with newline
const (
	DefaultListTemplate = `pmm-admin {{.Version}}

{{template "ServerInfo" .ServerInfo}}
{{printf "%-15s | %s" "Service Manager" .Platform}}
{{if .Err}}
{{.Err}}{{end}}{{if .Services}}
{{.Table}}{{end}}{{if .ExternalErr}}
{{.ExternalErr}}{{end}}{{if .ExternalServices}}
{{.ExternalTable}}{{end}}`
)

// List prints to stdout all services from Consul.
func (a *Admin) List() error {
	l := &List{
		Version:    Version,
		Platform:   service.Platform(),
		ServerInfo: a.serverInfo(),
	}

	defer func() {
		fmt.Print(l.Format(a.Format))
	}()

	var err error
	l.ExternalServices, err = a.ListExternalMetrics(context.TODO())
	if err != nil {
		l.ExternalErr = err.Error() + "\n"
	}

	node, _, err := a.consulAPI.Catalog().Node(a.Config.ClientName, nil)
	if err != nil || node == nil {
		l.Err = fmt.Sprintf("%s '%s'.\n", noMonitoring, a.Config.ClientName)
		return nil
	}

	if len(node.Services) == 0 {
		l.Err = fmt.Sprint("No services under monitoring.\n")
		return nil
	}

	// Get service data
	svcTable := a.getSVCTable(node)
	sort.Sort(sortOutput(svcTable))
	l.Services = svcTable

	return nil
}

func (a *Admin) getSVCTable(node *consul.CatalogNode) []ServiceStatus {
	// Parse all services except mysql:queries.
	var queryServices []*consul.AgentService
	var svcTable []ServiceStatus
	for _, svc := range node.Services {
		// When server hostname == client name, we have to exclude consul.
		if svc.Service == "consul" {
			continue
		}
		switch svc.Service {
		case "mysql:queries", "mongodb:queries":
			queryServices = append(queryServices, svc)
			continue
		}

		status := getServiceStatus(fmt.Sprintf("pmm-%s-%d", strings.Replace(svc.Service, ":", "-", 1), svc.Port))

		opts := []string{}
		name := "-"
		dsn := "-"
		// Get values for service from Consul KV.
		prefix := fmt.Sprintf("%s/%s/", a.Config.ClientName, svc.ID)
		if data, _, err := a.consulAPI.KV().List(prefix, nil); err == nil {
			for _, kvp := range data {
				key := kvp.Key[len(prefix):]
				switch key {
				case "dsn":
					dsn = string(kvp.Value)
				default:
					opts = append(opts, fmt.Sprintf("%s=%s", key, kvp.Value))
				}
			}
		}
		// Parse Consul service tags.
		for _, tag := range svc.Tags {
			if strings.HasPrefix(tag, "alias_") {
				name = tag[6:]
				continue
			}
			if tag == "scheme_https" {
				continue
			}
			tag := strings.Replace(tag, "_", "=", 1)
			opts = append(opts, tag)
		}

		row := ServiceStatus{
			Type:    svc.Service,
			Name:    name,
			Port:    fmt.Sprintf("%d", svc.Port),
			Running: status,
			DSN:     dsn,
			Options: strings.Join(opts, ", "),
		}
		svcTable = append(svcTable, row)
	}

	// Parse queries service.
	for _, queryService := range queryServices {
		status := getServiceStatus(fmt.Sprintf("pmm-%s-%d", strings.Replace(queryService.Service, ":", "-", 1), queryService.Port))

		// Get names from Consul tags.
		names := []string{}
		for _, tag := range queryService.Tags {
			if strings.HasPrefix(tag, "alias_") {
				names = append(names, tag[6:])
			}
		}

		for _, name := range names {
			dsn := "-"
			opts := []string{}
			// Get values for service from Consul KV.
			prefix := fmt.Sprintf("%s/%s/%s/", a.Config.ClientName, queryService.ID, name)
			if data, _, err := a.consulAPI.KV().List(prefix, nil); err == nil {
				for _, kvp := range data {
					key := kvp.Key[len(prefix):]
					switch key {
					case "dsn":
						dsn = string(kvp.Value)
					case "qan_mysql_uuid", "qan_mongodb_uuid":
						f := fmt.Sprintf("%s/config/qan-%s.conf", AgentBaseDir, kvp.Value)
						config, err := getProtoQAN(f)
						if err != nil {
							opts = append(opts, err.Error())
							continue
						}
						opts = append(opts, getQueriesOptions(config)...)
						if key == "qan_mysql_uuid" {
							opts = append(opts, getMySQLQueriesOptions(config)...)
						}
					}
				}
			}
			row := ServiceStatus{
				Type:    queryService.Service,
				Name:    name,
				Port:    "-",
				Running: status,
				DSN:     dsn,
				Options: strings.Join(opts, ", "),
			}
			svcTable = append(svcTable, row)
		}
	}

	return svcTable
}
<|endoftext|>


<commit_before>// Package log prints different kinds of log messages in the formats we like.
package log

import (
	"log"
	"os"
)

var (
	InfoLog  *log.Logger
	ErrorLog *log.Logger
)

func init() {
	InfoLog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	ErrorLog = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}

// Info logs an informational message to Stdout.
func Info(s string, v ...interface{}) {
	InfoLog.Printf(s, v...)
}

// Error logs an error to Stderr.
func Error(s string, v ...interface{}) {
	ErrorLog.Printf(s, v...)
}
<commit_msg>Include calling filename and line # in error logs<commit_after>// Package log prints different kinds of log messages in the formats we like.
package log

import (
	"fmt"
	"log"
	"os"
	"runtime"
)

var (
	InfoLog  *log.Logger
	ErrorLog *log.Logger
)

func init() {
	InfoLog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
	ErrorLog = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime)
}

// Info logs an informational message to Stdout.
func Info(s string, v ...interface{}) {
	InfoLog.Printf(s, v...)
}

// Error logs an error to Stderr.
func Error(s string, v ...interface{}) {
	// Include original caller information
	_, file, line, _ := runtime.Caller(1)

	// Determine short filename (from standard log package)
	short := file
	for i := len(file) - 1; i > 0; i-- {
		if file[i] == '/' {
			short = file[i+1:]
			break
		}
	}
	file = short

	ErrorLog.Printf(fmt.Sprintf("%s:%d: ", short, line)+s, v...)
}
<|endoftext|>


<commit_before>package log

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/pkg/errors"

	"github.com/openfresh/plasma/config"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func NewLogger(config config.Log) (*zap.Logger, error) {
	var writer io.Writer
	switch config.Out {
	case "stdout":
		writer = os.Stdout
	case "stderr":
		writer = os.Stderr
	case "discard":
		writer = ioutil.Discard
	default:
		w, err := os.OpenFile(config.Out, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to open file: %s", config.Out)
		}
		writer = w
	}

	var level zapcore.Level
	if err := level.UnmarshalText([]byte(config.Level)); err != nil {
		return nil, errors.Wrapf(err, "failed to unmarshal level %s", config.Level)
	}

	writerSyncer := zapcore.AddSync(writer)
	logger := zap.New(zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		zapcore.Lock(writerSyncer),
		level,
	), zap.ErrorOutput(writerSyncer))

	return logger, nil
}

func HttpRequestToLogFields(r *http.Request) []zapcore.Field {
	return []zapcore.Field{
		zap.String("user-agent", r.UserAgent()),
		zap.String("referer", r.Referer()),
		zap.Int64("content-length", r.ContentLength),
		zap.String("host", r.Host),
		zap.String("method", r.Method),
		zap.String("remote-addr", r.RemoteAddr),
	}
}
<commit_msg>support X-Forwarded-For header (#16)<commit_after>package log

import (
	"io"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/pkg/errors"

	"github.com/openfresh/plasma/config"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func NewLogger(config config.Log) (*zap.Logger, error) {
	var writer io.Writer
	switch config.Out {
	case "stdout":
		writer = os.Stdout
	case "stderr":
		writer = os.Stderr
	case "discard":
		writer = ioutil.Discard
	default:
		w, err := os.OpenFile(config.Out, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
		if err != nil {
			return nil, errors.Wrapf(err, "failed to open file: %s", config.Out)
		}
		writer = w
	}

	var level zapcore.Level
	if err := level.UnmarshalText([]byte(config.Level)); err != nil {
		return nil, errors.Wrapf(err, "failed to unmarshal level %s", config.Level)
	}

	writerSyncer := zapcore.AddSync(writer)
	logger := zap.New(zapcore.NewCore(
		zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
		zapcore.Lock(writerSyncer),
		level,
	), zap.ErrorOutput(writerSyncer))

	return logger, nil
}

func HttpRequestToLogFields(r *http.Request) []zapcore.Field {
	remoteAddr := r.RemoteAddr
	if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
		remoteAddr = addr
	}
	return []zapcore.Field{
		zap.String("user-agent", r.UserAgent()),
		zap.String("referer", r.Referer()),
		zap.Int64("content-length", r.ContentLength),
		zap.String("host", r.Host),
		zap.String("method", r.Method),
		zap.String("remote-addr", remoteAddr),
	}
}
<|endoftext|>


<commit_before>package logging // import "resenje.org/logging"

import (
	"container/ring"
	"errors"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

var (
	loggers = make(map[string]*Logger)
	lock    = &sync.RWMutex{}
)

// Logger is struct that is capable of processing log messages.
// It is central point of application logging configuration. Application
// can have multiple loggers with different names and at moment of logging
// logger is chosen.
//
// Each logger has level and ignores all log records below defined
// level. Also, each logger has array of handlers used to to actuall log
// record processing.
type Logger struct {
	Name          string
	Level         Level
	Handlers      []Handler
	buffer        *ring.Ring
	stateChannel  chan uint8
	recordChannel chan *Record
	lock          sync.RWMutex
	countIn       uint64
	countOut      uint64
}

// NewLogger creates and returns new logger instance with provided name, log level,
// handlers and buffer length.
// Name of logger has to be unique when it is created or error will be returned.
// Log level is lowest level or log record that this logger will handle.
// Log records (above defined log level) will be passed to all log handlers for processing.
func NewLogger(name string, level Level, handlers []Handler, bufferLength int) (*Logger, error) {
	lock.Lock()
	defer lock.Unlock()
	if _, ok := loggers[name]; ok {
		return nil, errors.New("Logger with that name already exists")
	}
	logger := &Logger{
		Name:          name,
		Level:         level,
		Handlers:      handlers,
		buffer:        ring.New(bufferLength),
		stateChannel:  make(chan uint8, 0),
		recordChannel: make(chan *Record, 2048),
		lock:          sync.RWMutex{},
		countOut:      0,
		countIn:       0,
	}
	go logger.run()
	loggers[name] = logger
	return logger, nil
}

// GetLogger returns logger instance based on provided name.
// If logger does not exist, error will be returned.
func GetLogger(name string) (*Logger, error) {
	logger, ok := loggers[name]
	if !ok {
		return nil, fmt.Errorf("Unknown logger %s", name)
	}
	return logger, nil
}

// RemoveLogger deletes logger from global logger registry.
func RemoveLogger(name string) {
	lock.Lock()
	defer lock.Unlock()
	delete(loggers, name)
}

// RemoveLoggers removes all loggers from global logger registry.
// After calling this, not loggers exist, so new ones has to be creaded
// for logging to work.
func RemoveLoggers() {
	lock.Lock()
	defer lock.Unlock()
	loggers = make(map[string]*Logger)
}

// WaitForAllUnprocessedRecords blocks execution until all unprocessed
// log records are processed.
// Since this library implements async logging, it is possible to have
// unprocessed logs at the moment when application is terminating.
// In that case, log messages can be lost. This mehtods blocks execution
// until all log records are processed, to ensure that all log messages
// are handled.
func WaitForAllUnprocessedRecords() {
	var wg sync.WaitGroup
	for _, logger := range loggers {
		wg.Add(1)
		go func(logger *Logger) {
			defer wg.Done()
			logger.WaitForUnprocessedRecords()
		}(logger)
	}
	wg.Wait()
}

// String returns logger name.
func (logger *Logger) String() string {
	return logger.Name
}

// run starts async log records processing.
func (logger *Logger) run() {
	defer func() {
		logger.WaitForUnprocessedRecords()
		logger.closeHandlers()
	}()
recordLoop:
	for {
		select {
		case record := <-logger.recordChannel:
			record.process()
		case state := <-logger.stateChannel:
			switch state {
			case stopped:
				break recordLoop
			case paused:
			stateLoop:
				for {
					select {
					case state := <-logger.stateChannel:
						switch state {
						case stopped:
							break recordLoop
						case running:
							break stateLoop
						default:
							continue
						}
					}
				}
			}
		}
	}
}

// WaitForUnprocessedRecords block execution until all unprocessed log records
// for this logger are processed.
// In order to wait for processing in all loggers, logging.WaitForAllUnprocessedRecords
// can be used.
func (logger *Logger) WaitForUnprocessedRecords() {
	runtime.Gosched()
	var (
		diff     uint64
		diffPrev uint64
		i        uint8
	)
	for {
		diff = atomic.LoadUint64(&logger.countIn) - atomic.LoadUint64(&logger.countOut)
		if diff == diffPrev {
			i++
		}
		if i >= 100 {
			return
		}
		if diff > 0 {
			diffPrev = diff
			time.Sleep(10 * time.Millisecond)
		} else {
			return
		}
	}
}

// closeHandlers calls Close for all handlers for this logger
// to release resources.
func (logger *Logger) closeHandlers() {
	for _, handler := range logger.Handlers {
		handler.Close()
	}
}

// Pause temporarily stops processing of log messages in current logger.
func (logger *Logger) Pause() {
	logger.stateChannel <- paused
}

// Unpause continues processing of log messages in current logger.
func (logger *Logger) Unpause() {
	logger.stateChannel <- running
}

// Stop permanently stops processing of log messages in current logger.
func (logger *Logger) Stop() {
	logger.stateChannel <- stopped
}

// SetBufferLength sets lenth of buffer for accepting log records.
func (logger *Logger) SetBufferLength(length int) {
	logger.lock.Lock()
	defer logger.lock.Unlock()

	if length == 0 {
		logger.buffer = nil
	} else if length != logger.buffer.Len() {
		logger.buffer = ring.New(length)
	}
}

// AddHandler add new handler to current logger.
func (logger *Logger) AddHandler(handler Handler) {
	logger.lock.Lock()
	defer logger.lock.Unlock()
	logger.Handlers = append(logger.Handlers, handler)
	logger.flushBuffer()
}

// ClearHandlers removes all handlers from current logger.
func (logger *Logger) ClearHandlers() {
	logger.lock.Lock()
	defer logger.lock.Unlock()
	logger.closeHandlers()
	logger.Handlers = make([]Handler, 0)
	logger.flushBuffer()
}

// SetLevel sets lowes level that current logger will process.
func (logger *Logger) SetLevel(level Level) {
	logger.lock.Lock()
	logger.Level = level
	logger.flushBuffer()
}

func (logger *Logger) flushBuffer() {
	if logger.buffer != nil {
		oldBuffer := logger.buffer
		logger.buffer = ring.New(oldBuffer.Len())

		go func() {
			oldBuffer.Do(func(x interface{}) {

				if x == nil {
					return
				}

				record := x.(*Record)

				atomic.AddUint64(&logger.countIn, 1)
				logger.recordChannel <- record
			})
		}()
	}
}

// log creates log record and submits it for processing.
func (logger *Logger) log(level Level, format string, a ...interface{}) {
	var message string
	if format == "" {
		message = fmt.Sprint(a...)
	} else {
		message = fmt.Sprintf(format, a...)
	}

	record := &Record{
		Level:   level,
		Message: message,
		Time:    time.Now(),
		logger:  logger,
	}
	atomic.AddUint64(&logger.countIn, 1)
	logger.recordChannel <- record
}

// Logf logs provided message with formatting.
func (logger *Logger) Logf(level Level, format string, a ...interface{}) {
	logger.log(level, format, a...)
}

// Log logs provided message.
func (logger *Logger) Log(level Level, a ...interface{}) {
	logger.log(level, "", a...)
}

// Emergencyf logs provided message with formatting in EMERGENCY level.
func (logger *Logger) Emergencyf(format string, a ...interface{}) {
	logger.log(EMERGENCY, format, a...)
}

// Emergency logs provided message in EMERGENCY level.
func (logger *Logger) Emergency(a ...interface{}) {
	logger.log(EMERGENCY, "", a...)
}

// Aleftf logs provided message with formatting in ALERT level.
func (logger *Logger) Alertf(format string, a ...interface{}) {
	logger.log(ALERT, format, a...)
}

// Aleft logs provided message in ALERT level.
func (logger *Logger) Alert(a ...interface{}) {
	logger.log(ALERT, "", a...)
}

// Criticalf logs provided message with formatting in CRITICAL level.
func (logger *Logger) Criticalf(format string, a ...interface{}) {
	logger.log(CRITICAL, format, a...)
}

// Critical logs provided message in CRITICAL level.
func (logger *Logger) Critical(a ...interface{}) {
	logger.log(CRITICAL, "", a...)
}

// Errorf logs provided message with formatting in ERROR level.
func (logger *Logger) Errorf(format string, a ...interface{}) {
	logger.log(ERROR, format, a...)
}

// Error logs provided message in ERROR level.
func (logger *Logger) Error(a ...interface{}) {
	logger.log(ERROR, "", a...)
}

// Warningf logs provided message with formatting in WARNING level.
func (logger *Logger) Warningf(format string, a ...interface{}) {
	logger.log(WARNING, format, a...)
}

// Warning logs provided message in WARNING level.
func (logger *Logger) Warning(a ...interface{}) {
	logger.log(WARNING, "", a...)
}

// Noticef logs provided message with formatting in NOTICE level.
func (logger *Logger) Noticef(format string, a ...interface{}) {
	logger.log(NOTICE, format, a...)
}

// Notice logs provided message in NOTICE level.
func (logger *Logger) Notice(a ...interface{}) {
	logger.log(NOTICE, "", a...)
}

// Infof logs provided message with formatting in INFO level.
func (logger *Logger) Infof(format string, a ...interface{}) {
	logger.log(INFO, format, a...)
}

// Info logs provided message in INFO level.
func (logger *Logger) Info(a ...interface{}) {
	logger.log(INFO, "", a...)
}

// Debugf logs provided message with formatting in DEBUG level.
func (logger *Logger) Debugf(format string, a ...interface{}) {
	logger.log(DEBUG, format, a...)
}

// Debug logs provided message in DEBUG level.
func (logger *Logger) Debug(a ...interface{}) {
	logger.log(DEBUG, "", a...)
}
<commit_msg>Lock WaitForAllUnprocessedRecords and GetLogger<commit_after>package logging // import "resenje.org/logging"

import (
	"container/ring"
	"errors"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

var (
	loggers = make(map[string]*Logger)
	lock    = &sync.RWMutex{}
)

// Logger is struct that is capable of processing log messages.
// It is central point of application logging configuration. Application
// can have multiple loggers with different names and at moment of logging
// logger is chosen.
//
// Each logger has level and ignores all log records below defined
// level. Also, each logger has array of handlers used to to actuall log
// record processing.
type Logger struct {
	Name          string
	Level         Level
	Handlers      []Handler
	buffer        *ring.Ring
	stateChannel  chan uint8
	recordChannel chan *Record
	lock          sync.RWMutex
	countIn       uint64
	countOut      uint64
}

// NewLogger creates and returns new logger instance with provided name, log level,
// handlers and buffer length.
// Name of logger has to be unique when it is created or error will be returned.
// Log level is lowest level or log record that this logger will handle.
// Log records (above defined log level) will be passed to all log handlers for processing.
func NewLogger(name string, level Level, handlers []Handler, bufferLength int) (*Logger, error) {
	lock.Lock()
	defer lock.Unlock()
	if _, ok := loggers[name]; ok {
		return nil, errors.New("Logger with that name already exists")
	}
	logger := &Logger{
		Name:          name,
		Level:         level,
		Handlers:      handlers,
		buffer:        ring.New(bufferLength),
		stateChannel:  make(chan uint8, 0),
		recordChannel: make(chan *Record, 2048),
		lock:          sync.RWMutex{},
		countOut:      0,
		countIn:       0,
	}
	go logger.run()
	loggers[name] = logger
	return logger, nil
}

// GetLogger returns logger instance based on provided name.
// If logger does not exist, error will be returned.
func GetLogger(name string) (*Logger, error) {
	lock.Lock()
	defer lock.Unlock()
	logger, ok := loggers[name]
	if !ok {
		return nil, fmt.Errorf("Unknown logger %s", name)
	}
	return logger, nil
}

// RemoveLogger deletes logger from global logger registry.
func RemoveLogger(name string) {
	lock.Lock()
	defer lock.Unlock()
	delete(loggers, name)
}

// RemoveLoggers removes all loggers from global logger registry.
// After calling this, not loggers exist, so new ones has to be creaded
// for logging to work.
func RemoveLoggers() {
	lock.Lock()
	defer lock.Unlock()
	loggers = make(map[string]*Logger)
}

// WaitForAllUnprocessedRecords blocks execution until all unprocessed
// log records are processed.
// Since this library implements async logging, it is possible to have
// unprocessed logs at the moment when application is terminating.
// In that case, log messages can be lost. This mehtods blocks execution
// until all log records are processed, to ensure that all log messages
// are handled.
func WaitForAllUnprocessedRecords() {
	lock.Lock()
	defer lock.Unlock()
	var wg sync.WaitGroup
	for _, logger := range loggers {
		wg.Add(1)
		go func(logger *Logger) {
			defer wg.Done()
			logger.WaitForUnprocessedRecords()
		}(logger)
	}
	wg.Wait()
}

// String returns logger name.
func (logger *Logger) String() string {
	return logger.Name
}

// run starts async log records processing.
func (logger *Logger) run() {
	defer func() {
		logger.WaitForUnprocessedRecords()
		logger.closeHandlers()
	}()
recordLoop:
	for {
		select {
		case record := <-logger.recordChannel:
			record.process()
		case state := <-logger.stateChannel:
			switch state {
			case stopped:
				break recordLoop
			case paused:
			stateLoop:
				for {
					select {
					case state := <-logger.stateChannel:
						switch state {
						case stopped:
							break recordLoop
						case running:
							break stateLoop
						default:
							continue
						}
					}
				}
			}
		}
	}
}

// WaitForUnprocessedRecords block execution until all unprocessed log records
// for this logger are processed.
// In order to wait for processing in all loggers, logging.WaitForAllUnprocessedRecords
// can be used.
func (logger *Logger) WaitForUnprocessedRecords() {
	runtime.Gosched()
	var (
		diff     uint64
		diffPrev uint64
		i        uint8
	)
	for {
		diff = atomic.LoadUint64(&logger.countIn) - atomic.LoadUint64(&logger.countOut)
		if diff == diffPrev {
			i++
		}
		if i >= 100 {
			return
		}
		if diff > 0 {
			diffPrev = diff
			time.Sleep(10 * time.Millisecond)
		} else {
			return
		}
	}
}

// closeHandlers calls Close for all handlers for this logger
// to release resources.
func (logger *Logger) closeHandlers() {
	for _, handler := range logger.Handlers {
		handler.Close()
	}
}

// Pause temporarily stops processing of log messages in current logger.
func (logger *Logger) Pause() {
	logger.stateChannel <- paused
}

// Unpause continues processing of log messages in current logger.
func (logger *Logger) Unpause() {
	logger.stateChannel <- running
}

// Stop permanently stops processing of log messages in current logger.
func (logger *Logger) Stop() {
	logger.stateChannel <- stopped
}

// SetBufferLength sets lenth of buffer for accepting log records.
func (logger *Logger) SetBufferLength(length int) {
	logger.lock.Lock()
	defer logger.lock.Unlock()

	if length == 0 {
		logger.buffer = nil
	} else if length != logger.buffer.Len() {
		logger.buffer = ring.New(length)
	}
}

// AddHandler add new handler to current logger.
func (logger *Logger) AddHandler(handler Handler) {
	logger.lock.Lock()
	defer logger.lock.Unlock()
	logger.Handlers = append(logger.Handlers, handler)
	logger.flushBuffer()
}

// ClearHandlers removes all handlers from current logger.
func (logger *Logger) ClearHandlers() {
	logger.lock.Lock()
	defer logger.lock.Unlock()
	logger.closeHandlers()
	logger.Handlers = make([]Handler, 0)
	logger.flushBuffer()
}

// SetLevel sets lowes level that current logger will process.
func (logger *Logger) SetLevel(level Level) {
	logger.lock.Lock()
	logger.Level = level
	logger.flushBuffer()
}

func (logger *Logger) flushBuffer() {
	if logger.buffer != nil {
		oldBuffer := logger.buffer
		logger.buffer = ring.New(oldBuffer.Len())

		go func() {
			oldBuffer.Do(func(x interface{}) {

				if x == nil {
					return
				}

				record := x.(*Record)

				atomic.AddUint64(&logger.countIn, 1)
				logger.recordChannel <- record
			})
		}()
	}
}

// log creates log record and submits it for processing.
func (logger *Logger) log(level Level, format string, a ...interface{}) {
	var message string
	if format == "" {
		message = fmt.Sprint(a...)
	} else {
		message = fmt.Sprintf(format, a...)
	}

	record := &Record{
		Level:   level,
		Message: message,
		Time:    time.Now(),
		logger:  logger,
	}
	atomic.AddUint64(&logger.countIn, 1)
	logger.recordChannel <- record
}

// Logf logs provided message with formatting.
func (logger *Logger) Logf(level Level, format string, a ...interface{}) {
	logger.log(level, format, a...)
}

// Log logs provided message.
func (logger *Logger) Log(level Level, a ...interface{}) {
	logger.log(level, "", a...)
}

// Emergencyf logs provided message with formatting in EMERGENCY level.
func (logger *Logger) Emergencyf(format string, a ...interface{}) {
	logger.log(EMERGENCY, format, a...)
}

// Emergency logs provided message in EMERGENCY level.
func (logger *Logger) Emergency(a ...interface{}) {
	logger.log(EMERGENCY, "", a...)
}

// Aleftf logs provided message with formatting in ALERT level.
func (logger *Logger) Alertf(format string, a ...interface{}) {
	logger.log(ALERT, format, a...)
}

// Aleft logs provided message in ALERT level.
func (logger *Logger) Alert(a ...interface{}) {
	logger.log(ALERT, "", a...)
}

// Criticalf logs provided message with formatting in CRITICAL level.
func (logger *Logger) Criticalf(format string, a ...interface{}) {
	logger.log(CRITICAL, format, a...)
}

// Critical logs provided message in CRITICAL level.
func (logger *Logger) Critical(a ...interface{}) {
	logger.log(CRITICAL, "", a...)
}

// Errorf logs provided message with formatting in ERROR level.
func (logger *Logger) Errorf(format string, a ...interface{}) {
	logger.log(ERROR, format, a...)
}

// Error logs provided message in ERROR level.
func (logger *Logger) Error(a ...interface{}) {
	logger.log(ERROR, "", a...)
}

// Warningf logs provided message with formatting in WARNING level.
func (logger *Logger) Warningf(format string, a ...interface{}) {
	logger.log(WARNING, format, a...)
}

// Warning logs provided message in WARNING level.
func (logger *Logger) Warning(a ...interface{}) {
	logger.log(WARNING, "", a...)
}

// Noticef logs provided message with formatting in NOTICE level.
func (logger *Logger) Noticef(format string, a ...interface{}) {
	logger.log(NOTICE, format, a...)
}

// Notice logs provided message in NOTICE level.
func (logger *Logger) Notice(a ...interface{}) {
	logger.log(NOTICE, "", a...)
}

// Infof logs provided message with formatting in INFO level.
func (logger *Logger) Infof(format string, a ...interface{}) {
	logger.log(INFO, format, a...)
}

// Info logs provided message in INFO level.
func (logger *Logger) Info(a ...interface{}) {
	logger.log(INFO, "", a...)
}

// Debugf logs provided message with formatting in DEBUG level.
func (logger *Logger) Debugf(format string, a ...interface{}) {
	logger.log(DEBUG, format, a...)
}

// Debug logs provided message in DEBUG level.
func (logger *Logger) Debug(a ...interface{}) {
	logger.log(DEBUG, "", a...)
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"io"
	"log/syslog"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

type (
	Color int
	Level int
)

// Colors for different log levels.
const (
	BLACK Color = (iota + 30)
	RED
	GREEN
	YELLOW
	BLUE
	MAGENTA
	CYAN
	WHITE
)

// Logging levels.
const (
	CRITICAL Level = iota
	ERROR
	WARNING
	NOTICE
	INFO
	DEBUG
)

var LevelNames = map[Level]string{
	CRITICAL: "CRITICAL",
	ERROR:    "ERROR",
	WARNING:  "WARNING",
	NOTICE:   "NOTICE",
	INFO:     "INFO",
	DEBUG:    "DEBUG",
}

var LevelColors = map[Level]Color{
	CRITICAL: MAGENTA,
	ERROR:    RED,
	WARNING:  YELLOW,
	NOTICE:   GREEN,
	INFO:     WHITE,
	DEBUG:    CYAN,
}

var (
	DefaultLogger    = NewLogger(procName())
	DefaultLevel     = INFO
	DefaultHandler   = StderrHandler
	DefaultFormatter = &defaultFormatter{}
	StdoutHandler    = NewWriterHandler(os.Stdout)
	StderrHandler    = NewWriterHandler(os.Stderr)
)

func init() {
	StdoutHandler.Colorize = true
	StderrHandler.Colorize = true
}

// Logger is the interface for outputing log messages in different levels.
// A new Logger can be created with NewLogger() function.
// You can changed the output handler with SetHandler() function.
type Logger interface {
	// SetLevel changes the level of the logger. Default is logging.Info.
	SetLevel(Level)

	// SetHandler replaces the current handler for output. Default is logging.StderrHandler.
	SetHandler(Handler)

	// SetCallDepth sets the parameter passed to runtime.Caller().
	// It is used to get the file name from call stack.
	// For example you need to set it to 1 if you are using a wrapper around
	// the Logger. Default value is zero.
	SetCallDepth(int)

	// Fatal is equivalent to l.Critical followed by a call to os.Exit(1).
	Fatal(format string, args ...interface{})

	// Panic is equivalent to l.Critical followed by a call to panic().
	Panic(format string, args ...interface{})

	// Critical logs a message using CRITICAL as log level.
	Critical(format string, args ...interface{})

	// Error logs a message using ERROR as log level.
	Error(format string, args ...interface{})

	// Warning logs a message using WARNING as log level.
	Warning(format string, args ...interface{})

	// Notice logs a message using NOTICE as log level.
	Notice(format string, args ...interface{})

	// Info logs a message using INFO as log level.
	Info(format string, args ...interface{})

	// Debug logs a message using DEBUG as log level.
	Debug(format string, args ...interface{})
}

// Handler handles the output.
type Handler interface {
	SetFormatter(Formatter)
	SetLevel(Level)

	// Handle single log record.
	Handle(*Record)

	// Close the handler.
	Close()
}

// Record contains all of the information about a single log message.
type Record struct {
	Format      string        // Format string
	Args        []interface{} // Arguments to format string
	LoggerName  string        // Name of the logger module
	Level       Level         // Level of the record
	Time        time.Time     // Time of the record (local time)
	Filename    string        // File name of the log call (absolute path)
	Line        int           // Line number in file
	ProcessID   int           // PID
	ProcessName string        // Name of the process
}

// Formatter formats a record.
type Formatter interface {
	// Format the record and return a message.
	Format(*Record) (message string)
}

///////////////////////
//                   //
// Default Formatter //
//                   //
///////////////////////

type defaultFormatter struct{}

// Format outputs a message like "2014-02-28 18:15:57 [example] INFO     something happened"
func (f *defaultFormatter) Format(rec *Record) string {
	return fmt.Sprintf("%s [%s] %-8s %s", fmt.Sprint(rec.Time)[:19], rec.LoggerName, LevelNames[rec.Level], fmt.Sprintf(rec.Format, rec.Args...))
}

///////////////////////////
//                       //
// Logger implementation //
//                       //
///////////////////////////

// logger is the default Logger implementation.
type logger struct {
	Name      string
	Level     Level
	Handler   Handler
	calldepth int
}

// NewLogger returns a new Logger implementation. Do not forget to close it at exit.
func NewLogger(name string) Logger {
	return &logger{
		Name:    name,
		Level:   DefaultLevel,
		Handler: DefaultHandler,
	}
}

func (l *logger) Close() {
	l.Handler.Close()
}

func (l *logger) SetLevel(level Level) {
	l.Level = level
}

func (l *logger) SetHandler(b Handler) {
	l.Handler = b
}

func (l *logger) SetCallDepth(n int) {
	l.calldepth = n
}

func (l *logger) Fatal(format string, args ...interface{}) {
	l.Critical(format, args...)
	l.Close()
	os.Exit(1)
}

func (l *logger) Panic(format string, args ...interface{}) {
	l.Critical(format, args...)
	l.Close()
	panic(fmt.Sprintf(format, args...))
}

func (l *logger) Critical(format string, args ...interface{}) {
	if l.Level >= CRITICAL {
		l.log(CRITICAL, format, args...)
	}
}

func (l *logger) Error(format string, args ...interface{}) {
	if l.Level >= ERROR {
		l.log(ERROR, format, args...)
	}
}

func (l *logger) Warning(format string, args ...interface{}) {
	if l.Level >= WARNING {
		l.log(WARNING, format, args...)
	}
}

func (l *logger) Notice(format string, args ...interface{}) {
	if l.Level >= NOTICE {
		l.log(NOTICE, format, args...)
	}
}

func (l *logger) Info(format string, args ...interface{}) {
	if l.Level >= INFO {
		l.log(INFO, format, args...)
	}
}

func (l *logger) Debug(format string, args ...interface{}) {
	if l.Level >= DEBUG {
		l.log(DEBUG, format, args...)
	}
}

func (l *logger) log(level Level, format string, args ...interface{}) {
	// Add missing newline at the end.
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}

	_, file, line, ok := runtime.Caller(l.calldepth + 2)
	if !ok {
		file = "???"
		line = 0
	}

	rec := &Record{
		Format:      format,
		Args:        args,
		LoggerName:  l.Name,
		Level:       level,
		Time:        time.Now(),
		Filename:    file,
		Line:        line,
		ProcessName: procName(),
		ProcessID:   os.Getpid(),
	}

	l.Handler.Handle(rec)
}

// procName returns the name of the current process.
func procName() string { return filepath.Base(os.Args[0]) }

///////////////////
//               //
// DefaultLogger //
//               //
///////////////////

func Fatal(format string, args ...interface{}) {
	DefaultLogger.Fatal(format, args...)
}

func Panic(format string, args ...interface{}) {
	DefaultLogger.Panic(format, args...)
}

func Critical(format string, args ...interface{}) {
	DefaultLogger.Critical(format, args...)
}

func Error(format string, args ...interface{}) {
	DefaultLogger.Error(format, args...)
}

func Warning(format string, args ...interface{}) {
	DefaultLogger.Warning(format, args...)
}

func Notice(format string, args ...interface{}) {
	DefaultLogger.Notice(format, args...)
}

func Info(format string, args ...interface{}) {
	DefaultLogger.Info(format, args...)
}

func Debug(format string, args ...interface{}) {
	DefaultLogger.Debug(format, args...)
}

/////////////////
//             //
// BaseHandler //
//             //
/////////////////

type BaseHandler struct {
	Level     Level
	Formatter Formatter
}

func NewBaseHandler() *BaseHandler {
	return &BaseHandler{
		Level:     DefaultLevel,
		Formatter: DefaultFormatter,
	}
}

func (h *BaseHandler) SetLevel(l Level) {
	h.Level = l
}

func (h *BaseHandler) SetFormatter(f Formatter) {
	h.Formatter = f
}

func (h *BaseHandler) FilterAndFormat(rec *Record) string {
	if h.Level >= rec.Level {
		return h.Formatter.Format(rec)
	}
	return ""
}

///////////////////
//               //
// WriterHandler //
//               //
///////////////////

// WriterHandler is a handler implementation that writes the logging output to a io.Writer.
type WriterHandler struct {
	*BaseHandler
	w        io.Writer
	Colorize bool
}

func NewWriterHandler(w io.Writer) *WriterHandler {
	return &WriterHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}
}

func (b *WriterHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}
	if b.Colorize {
		b.w.Write([]byte(fmt.Sprintf("\033[%dm", LevelColors[rec.Level])))
	}
	fmt.Fprint(b.w, message)
	if b.Colorize {
		b.w.Write([]byte("\033[0m")) // reset color
	}
}

func (b *WriterHandler) Close() {}

///////////////////
//               //
// SyslogHandler //
//               //
///////////////////

// SyslogHandler sends the logging output to syslog.
type SyslogHandler struct {
	*BaseHandler
	w *syslog.Writer
}

func NewSyslogHandler(tag string) (*SyslogHandler, error) {
	// Priority in New constructor is not important here because we
	// do not use w.Write() directly.
	w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_USER, tag)
	if err != nil {
		return nil, err
	}
	return &SyslogHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}, nil
}

func (b *SyslogHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}

	var fn func(string) error
	switch rec.Level {
	case CRITICAL:
		fn = b.w.Crit
	case ERROR:
		fn = b.w.Err
	case WARNING:
		fn = b.w.Warning
	case NOTICE:
		fn = b.w.Notice
	case INFO:
		fn = b.w.Info
	case DEBUG:
		fn = b.w.Debug
	}
	fn(message)
}

func (b *SyslogHandler) Close() {
	b.w.Close()
}

//////////////////
//              //
// MultiHandler //
//              //
//////////////////

// MultiHandler sends the log output to multiple handlers concurrently.
type MultiHandler struct {
	handlers []Handler
}

func NewMultiHandler(handlers ...Handler) *MultiHandler {
	return &MultiHandler{handlers: handlers}
}

func (b *MultiHandler) SetFormatter(f Formatter) {
	for _, h := range b.handlers {
		h.SetFormatter(f)
	}
}

func (b *MultiHandler) SetLevel(l Level) {
	for _, h := range b.handlers {
		h.SetLevel(l)
	}
}

func (b *MultiHandler) Handle(rec *Record) {
	wg := sync.WaitGroup{}
	wg.Add(len(b.handlers))
	for _, handler := range b.handlers {
		go func(handler Handler) {
			handler.Handle(rec)
			wg.Done()
		}(handler)
	}
	wg.Wait()
}

func (b *MultiHandler) Close() {
	wg := sync.WaitGroup{}
	wg.Add(len(b.handlers))
	for _, handler := range b.handlers {
		go func(handler Handler) {
			handler.Close()
			wg.Done()
		}(handler)
	}
	wg.Wait()
}
<commit_msg>remove logger.Close()<commit_after>package logging

import (
	"fmt"
	"io"
	"log/syslog"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

type (
	Color int
	Level int
)

// Colors for different log levels.
const (
	BLACK Color = (iota + 30)
	RED
	GREEN
	YELLOW
	BLUE
	MAGENTA
	CYAN
	WHITE
)

// Logging levels.
const (
	CRITICAL Level = iota
	ERROR
	WARNING
	NOTICE
	INFO
	DEBUG
)

var LevelNames = map[Level]string{
	CRITICAL: "CRITICAL",
	ERROR:    "ERROR",
	WARNING:  "WARNING",
	NOTICE:   "NOTICE",
	INFO:     "INFO",
	DEBUG:    "DEBUG",
}

var LevelColors = map[Level]Color{
	CRITICAL: MAGENTA,
	ERROR:    RED,
	WARNING:  YELLOW,
	NOTICE:   GREEN,
	INFO:     WHITE,
	DEBUG:    CYAN,
}

var (
	DefaultLogger    = NewLogger(procName())
	DefaultLevel     = INFO
	DefaultHandler   = StderrHandler
	DefaultFormatter = &defaultFormatter{}
	StdoutHandler    = NewWriterHandler(os.Stdout)
	StderrHandler    = NewWriterHandler(os.Stderr)
)

func init() {
	StdoutHandler.Colorize = true
	StderrHandler.Colorize = true
}

// Logger is the interface for outputing log messages in different levels.
// A new Logger can be created with NewLogger() function.
// You can changed the output handler with SetHandler() function.
type Logger interface {
	// SetLevel changes the level of the logger. Default is logging.Info.
	SetLevel(Level)

	// SetHandler replaces the current handler for output. Default is logging.StderrHandler.
	SetHandler(Handler)

	// SetCallDepth sets the parameter passed to runtime.Caller().
	// It is used to get the file name from call stack.
	// For example you need to set it to 1 if you are using a wrapper around
	// the Logger. Default value is zero.
	SetCallDepth(int)

	// Fatal is equivalent to l.Critical followed by a call to os.Exit(1).
	Fatal(format string, args ...interface{})

	// Panic is equivalent to l.Critical followed by a call to panic().
	Panic(format string, args ...interface{})

	// Critical logs a message using CRITICAL as log level.
	Critical(format string, args ...interface{})

	// Error logs a message using ERROR as log level.
	Error(format string, args ...interface{})

	// Warning logs a message using WARNING as log level.
	Warning(format string, args ...interface{})

	// Notice logs a message using NOTICE as log level.
	Notice(format string, args ...interface{})

	// Info logs a message using INFO as log level.
	Info(format string, args ...interface{})

	// Debug logs a message using DEBUG as log level.
	Debug(format string, args ...interface{})
}

// Handler handles the output.
type Handler interface {
	SetFormatter(Formatter)
	SetLevel(Level)

	// Handle single log record.
	Handle(*Record)

	// Close the handler.
	Close()
}

// Record contains all of the information about a single log message.
type Record struct {
	Format      string        // Format string
	Args        []interface{} // Arguments to format string
	LoggerName  string        // Name of the logger module
	Level       Level         // Level of the record
	Time        time.Time     // Time of the record (local time)
	Filename    string        // File name of the log call (absolute path)
	Line        int           // Line number in file
	ProcessID   int           // PID
	ProcessName string        // Name of the process
}

// Formatter formats a record.
type Formatter interface {
	// Format the record and return a message.
	Format(*Record) (message string)
}

///////////////////////
//                   //
// Default Formatter //
//                   //
///////////////////////

type defaultFormatter struct{}

// Format outputs a message like "2014-02-28 18:15:57 [example] INFO     something happened"
func (f *defaultFormatter) Format(rec *Record) string {
	return fmt.Sprintf("%s [%s] %-8s %s", fmt.Sprint(rec.Time)[:19], rec.LoggerName, LevelNames[rec.Level], fmt.Sprintf(rec.Format, rec.Args...))
}

///////////////////////////
//                       //
// Logger implementation //
//                       //
///////////////////////////

// logger is the default Logger implementation.
type logger struct {
	Name      string
	Level     Level
	Handler   Handler
	calldepth int
}

// NewLogger returns a new Logger implementation. Do not forget to close it at exit.
func NewLogger(name string) Logger {
	return &logger{
		Name:    name,
		Level:   DefaultLevel,
		Handler: DefaultHandler,
	}
}

func (l *logger) SetLevel(level Level) {
	l.Level = level
}

func (l *logger) SetHandler(b Handler) {
	l.Handler = b
}

func (l *logger) SetCallDepth(n int) {
	l.calldepth = n
}

func (l *logger) Fatal(format string, args ...interface{}) {
	l.Critical(format, args...)
	os.Exit(1)
}

func (l *logger) Panic(format string, args ...interface{}) {
	l.Critical(format, args...)
	panic(fmt.Sprintf(format, args...))
}

func (l *logger) Critical(format string, args ...interface{}) {
	if l.Level >= CRITICAL {
		l.log(CRITICAL, format, args...)
	}
}

func (l *logger) Error(format string, args ...interface{}) {
	if l.Level >= ERROR {
		l.log(ERROR, format, args...)
	}
}

func (l *logger) Warning(format string, args ...interface{}) {
	if l.Level >= WARNING {
		l.log(WARNING, format, args...)
	}
}

func (l *logger) Notice(format string, args ...interface{}) {
	if l.Level >= NOTICE {
		l.log(NOTICE, format, args...)
	}
}

func (l *logger) Info(format string, args ...interface{}) {
	if l.Level >= INFO {
		l.log(INFO, format, args...)
	}
}

func (l *logger) Debug(format string, args ...interface{}) {
	if l.Level >= DEBUG {
		l.log(DEBUG, format, args...)
	}
}

func (l *logger) log(level Level, format string, args ...interface{}) {
	// Add missing newline at the end.
	if !strings.HasSuffix(format, "\n") {
		format += "\n"
	}

	_, file, line, ok := runtime.Caller(l.calldepth + 2)
	if !ok {
		file = "???"
		line = 0
	}

	rec := &Record{
		Format:      format,
		Args:        args,
		LoggerName:  l.Name,
		Level:       level,
		Time:        time.Now(),
		Filename:    file,
		Line:        line,
		ProcessName: procName(),
		ProcessID:   os.Getpid(),
	}

	l.Handler.Handle(rec)
}

// procName returns the name of the current process.
func procName() string { return filepath.Base(os.Args[0]) }

///////////////////
//               //
// DefaultLogger //
//               //
///////////////////

func Fatal(format string, args ...interface{}) {
	DefaultLogger.Fatal(format, args...)
}

func Panic(format string, args ...interface{}) {
	DefaultLogger.Panic(format, args...)
}

func Critical(format string, args ...interface{}) {
	DefaultLogger.Critical(format, args...)
}

func Error(format string, args ...interface{}) {
	DefaultLogger.Error(format, args...)
}

func Warning(format string, args ...interface{}) {
	DefaultLogger.Warning(format, args...)
}

func Notice(format string, args ...interface{}) {
	DefaultLogger.Notice(format, args...)
}

func Info(format string, args ...interface{}) {
	DefaultLogger.Info(format, args...)
}

func Debug(format string, args ...interface{}) {
	DefaultLogger.Debug(format, args...)
}

/////////////////
//             //
// BaseHandler //
//             //
/////////////////

type BaseHandler struct {
	Level     Level
	Formatter Formatter
}

func NewBaseHandler() *BaseHandler {
	return &BaseHandler{
		Level:     DefaultLevel,
		Formatter: DefaultFormatter,
	}
}

func (h *BaseHandler) SetLevel(l Level) {
	h.Level = l
}

func (h *BaseHandler) SetFormatter(f Formatter) {
	h.Formatter = f
}

func (h *BaseHandler) FilterAndFormat(rec *Record) string {
	if h.Level >= rec.Level {
		return h.Formatter.Format(rec)
	}
	return ""
}

///////////////////
//               //
// WriterHandler //
//               //
///////////////////

// WriterHandler is a handler implementation that writes the logging output to a io.Writer.
type WriterHandler struct {
	*BaseHandler
	w        io.Writer
	Colorize bool
}

func NewWriterHandler(w io.Writer) *WriterHandler {
	return &WriterHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}
}

func (b *WriterHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}
	if b.Colorize {
		b.w.Write([]byte(fmt.Sprintf("\033[%dm", LevelColors[rec.Level])))
	}
	fmt.Fprint(b.w, message)
	if b.Colorize {
		b.w.Write([]byte("\033[0m")) // reset color
	}
}

func (b *WriterHandler) Close() {}

///////////////////
//               //
// SyslogHandler //
//               //
///////////////////

// SyslogHandler sends the logging output to syslog.
type SyslogHandler struct {
	*BaseHandler
	w *syslog.Writer
}

func NewSyslogHandler(tag string) (*SyslogHandler, error) {
	// Priority in New constructor is not important here because we
	// do not use w.Write() directly.
	w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_USER, tag)
	if err != nil {
		return nil, err
	}
	return &SyslogHandler{
		BaseHandler: NewBaseHandler(),
		w:           w,
	}, nil
}

func (b *SyslogHandler) Handle(rec *Record) {
	message := b.BaseHandler.FilterAndFormat(rec)
	if message == "" {
		return
	}

	var fn func(string) error
	switch rec.Level {
	case CRITICAL:
		fn = b.w.Crit
	case ERROR:
		fn = b.w.Err
	case WARNING:
		fn = b.w.Warning
	case NOTICE:
		fn = b.w.Notice
	case INFO:
		fn = b.w.Info
	case DEBUG:
		fn = b.w.Debug
	}
	fn(message)
}

func (b *SyslogHandler) Close() {
	b.w.Close()
}

//////////////////
//              //
// MultiHandler //
//              //
//////////////////

// MultiHandler sends the log output to multiple handlers concurrently.
type MultiHandler struct {
	handlers []Handler
}

func NewMultiHandler(handlers ...Handler) *MultiHandler {
	return &MultiHandler{handlers: handlers}
}

func (b *MultiHandler) SetFormatter(f Formatter) {
	for _, h := range b.handlers {
		h.SetFormatter(f)
	}
}

func (b *MultiHandler) SetLevel(l Level) {
	for _, h := range b.handlers {
		h.SetLevel(l)
	}
}

func (b *MultiHandler) Handle(rec *Record) {
	wg := sync.WaitGroup{}
	wg.Add(len(b.handlers))
	for _, handler := range b.handlers {
		go func(handler Handler) {
			handler.Handle(rec)
			wg.Done()
		}(handler)
	}
	wg.Wait()
}

func (b *MultiHandler) Close() {
	wg := sync.WaitGroup{}
	wg.Add(len(b.handlers))
	for _, handler := range b.handlers {
		go func(handler Handler) {
			handler.Close()
			wg.Done()
		}(handler)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
	"github.com/google/mtail/metrics"
	"github.com/google/mtail/tailer"
	"github.com/google/mtail/watcher"
	"github.com/spf13/afero"
)

func TestNewLoader(t *testing.T) {
	w := watcher.NewFakeWatcher()
	store := metrics.NewStore()
	inLines := make(chan *tailer.LogLine)
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, inLines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}
	done := make(chan struct{})
	outLines := make(chan *tailer.LogLine)
	handle := &vmHandle{outLines, done}
	l.handleMu.Lock()
	l.handles["test"] = handle
	l.handleMu.Unlock()
	go func() {
		for _ = range outLines {
		}
		close(done)
	}()
	close(inLines)
	<-outLines
}

func TestCompileAndRun(t *testing.T) {
	var testProgram = "/$/ {}\n"
	store := metrics.NewStore()
	lines := make(chan *tailer.LogLine)
	w := watcher.NewFakeWatcher()
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, lines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}
	if err := l.CompileAndRun("Test", strings.NewReader(testProgram)); err != nil {
		t.Errorf("CompileAndRun returned error: %s", err)
	}
	l.handleMu.Lock()
	if len(l.handles) < 1 {
		t.Errorf("no vm handles: %v", l.handles)
	}
	l.handleMu.Unlock()
	l.handleMu.Lock()
	c := l.handles["Test"].done
	if c == nil {
		t.Errorf("No done channel in handles: %v", l.handles)
	}
	l.handleMu.Unlock()
	close(lines)
	<-c
	{
		l.handleMu.Lock()
		defer l.handleMu.Unlock()
		if len(l.handles) != 0 {
			t.Errorf("some vm handles: %v", l.handles)
		}
	}
}

var testProcessEvents = []struct {
	name             string
	events           []watcher.Event
	expectedPrograms []string
}{
	{"load",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"}},
		[]string{"foo.mtail"}},
	{"unload",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"},
			watcher.DeleteEvent{Pathname: "foo.mtail"}},
		[]string{}},
	{"reload",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"}},
		[]string{"foo.mtail"}},
	{"bad extension",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail.dpkg-dist"},
			watcher.UpdateEvent{Pathname: "foo.mtail.dpkg-dist"}},
		[]string{}},
	{"not exist",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "notexist.mtail"},
			watcher.UpdateEvent{Pathname: "notexist.mtail"}},
		[]string{}},
}

var testProgram = "/$/ {}\n"

func TestProcessEvents(t *testing.T) {
	for _, tt := range testProcessEvents {
		t.Logf("Starting %s", tt.name)
		w := watcher.NewFakeWatcher()
		w.Add(".")
		store := metrics.NewStore()
		lines := make(chan *tailer.LogLine)
		fs := afero.NewMemMapFs()
		o := LoaderOptions{store, lines, w, fs, false, false, false, false, true, false}
		l, err := NewLoader(o)
		if err != nil {
			t.Fatalf("couldn't create loader: %s", err)
		}
		for i := range tt.events {
			e := tt.events[i]
			switch e := e.(type) {
			case watcher.CreateEvent:
				if e.Pathname != "notexist.mtail" {
					_, err := fs.Create(e.Pathname)
					if err != nil {
						t.Fatalf("Create failed for %s: %s", e.Pathname, err)
					}
				}
				w.InjectCreate(e.Pathname)
			case watcher.DeleteEvent:
				err := fs.Remove(e.Pathname)
				if err != nil {
					t.Fatalf("Remove failed for %s: %s", e.Pathname, err)
				}
				w.InjectDelete(e.Pathname)
			case watcher.UpdateEvent:
				if e.Pathname != "notexist.mtail" {
					f, err := fs.Create(e.Pathname)
					if err != nil {
						t.Fatalf("Couldn't open file %s for test: %s", e.Pathname, err)
					}
					_, err = f.WriteString(testProgram)
					if err != nil {
						t.Fatalf("Couldn't write file contents: %s", err)
					}
					if err = f.Close(); err != nil {
						t.Fatalf("Close failed: %s", err)
					}
				}
				w.InjectUpdate(e.Pathname)
			}
		}
		w.Close()
		<-l.watcherDone
		l.handleMu.RLock()
		programs := make([]string, 0)
		for program := range l.handles {
			programs = append(programs, program)
		}
		l.handleMu.RUnlock()
		l.handleMu.RLock()
		if diff := deep.Equal(tt.expectedPrograms, programs); diff != nil {
			t.Errorf("%q: loaded programs don't match.\nl.handles: %+#v\n%s", tt.name, l.handles, diff)
		}
		l.handleMu.RUnlock()
		close(lines)

	}
}

var testProgFiles = []string{
	"test.wrongext",
	"test.mtail",
	".test",
}

func TestLoadProg(t *testing.T) {
	w := watcher.NewFakeWatcher()
	store := metrics.NewStore()
	inLines := make(chan *tailer.LogLine)
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, inLines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}

	for _, f := range testProgFiles {
		afero.WriteFile(fs, f, []byte(testProgram), 0644)
		err = l.LoadProg(f)
		if err != nil {
			t.Fatalf("couldn't load file: %s error: %s", f, err)
		}
	}
}
<commit_msg>Parallelises and subtestifies loader TestProcessEvents.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package vm

import (
	"strings"
	"testing"

	"github.com/go-test/deep"
	"github.com/google/mtail/metrics"
	"github.com/google/mtail/tailer"
	"github.com/google/mtail/watcher"
	"github.com/spf13/afero"
)

func TestNewLoader(t *testing.T) {
	w := watcher.NewFakeWatcher()
	store := metrics.NewStore()
	inLines := make(chan *tailer.LogLine)
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, inLines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}
	done := make(chan struct{})
	outLines := make(chan *tailer.LogLine)
	handle := &vmHandle{outLines, done}
	l.handleMu.Lock()
	l.handles["test"] = handle
	l.handleMu.Unlock()
	go func() {
		for _ = range outLines {
		}
		close(done)
	}()
	close(inLines)
	<-outLines
}

func TestCompileAndRun(t *testing.T) {
	var testProgram = "/$/ {}\n"
	store := metrics.NewStore()
	lines := make(chan *tailer.LogLine)
	w := watcher.NewFakeWatcher()
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, lines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}
	if err := l.CompileAndRun("Test", strings.NewReader(testProgram)); err != nil {
		t.Errorf("CompileAndRun returned error: %s", err)
	}
	l.handleMu.Lock()
	if len(l.handles) < 1 {
		t.Errorf("no vm handles: %v", l.handles)
	}
	l.handleMu.Unlock()
	l.handleMu.Lock()
	c := l.handles["Test"].done
	if c == nil {
		t.Errorf("No done channel in handles: %v", l.handles)
	}
	l.handleMu.Unlock()
	close(lines)
	<-c
	{
		l.handleMu.Lock()
		defer l.handleMu.Unlock()
		if len(l.handles) != 0 {
			t.Errorf("some vm handles: %v", l.handles)
		}
	}
}

var testProcessEvents = []struct {
	name             string
	events           []watcher.Event
	expectedPrograms []string
}{
	{"load",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"}},
		[]string{"foo.mtail"}},
	{"unload",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"},
			watcher.DeleteEvent{Pathname: "foo.mtail"}},
		[]string{}},
	{"reload",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"},
			watcher.UpdateEvent{Pathname: "foo.mtail"}},
		[]string{"foo.mtail"}},
	{"bad extension",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "foo.mtail.dpkg-dist"},
			watcher.UpdateEvent{Pathname: "foo.mtail.dpkg-dist"}},
		[]string{}},
	{"not exist",
		[]watcher.Event{
			watcher.CreateEvent{Pathname: "notexist.mtail"},
			watcher.UpdateEvent{Pathname: "notexist.mtail"}},
		[]string{}},
}

var testProgram = "/$/ {}\n"

func TestProcessEvents(t *testing.T) {
	for _, tt := range testProcessEvents {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			w := watcher.NewFakeWatcher()
			w.Add(".")
			store := metrics.NewStore()
			lines := make(chan *tailer.LogLine)
			fs := afero.NewMemMapFs()
			o := LoaderOptions{store, lines, w, fs, false, false, false, false, true, false}
			l, err := NewLoader(o)
			if err != nil {
				t.Fatalf("couldn't create loader: %s", err)
			}
			for i := range tt.events {
				e := tt.events[i]
				switch e := e.(type) {
				case watcher.CreateEvent:
					if e.Pathname != "notexist.mtail" {
						_, err := fs.Create(e.Pathname)
						if err != nil {
							t.Fatalf("Create failed for %s: %s", e.Pathname, err)
						}
					}
					w.InjectCreate(e.Pathname)
				case watcher.DeleteEvent:
					err := fs.Remove(e.Pathname)
					if err != nil {
						t.Fatalf("Remove failed for %s: %s", e.Pathname, err)
					}
					w.InjectDelete(e.Pathname)
				case watcher.UpdateEvent:
					if e.Pathname != "notexist.mtail" {
						f, err := fs.Create(e.Pathname)
						if err != nil {
							t.Fatalf("Couldn't open file %s for test: %s", e.Pathname, err)
						}
						_, err = f.WriteString(testProgram)
						if err != nil {
							t.Fatalf("Couldn't write file contents: %s", err)
						}
						if err = f.Close(); err != nil {
							t.Fatalf("Close failed: %s", err)
						}
					}
					w.InjectUpdate(e.Pathname)
				}
			}
			w.Close()
			<-l.watcherDone
			l.handleMu.RLock()
			programs := make([]string, 0)
			for program := range l.handles {
				programs = append(programs, program)
			}
			l.handleMu.RUnlock()
			l.handleMu.RLock()
			if diff := deep.Equal(tt.expectedPrograms, programs); diff != nil {
				t.Errorf("%q: loaded programs don't match.\nl.handles: %+#v\n%s", tt.name, l.handles, diff)
			}
			l.handleMu.RUnlock()
			close(lines)
		})
	}
}

var testProgFiles = []string{
	"test.wrongext",
	"test.mtail",
	".test",
}

func TestLoadProg(t *testing.T) {
	w := watcher.NewFakeWatcher()
	store := metrics.NewStore()
	inLines := make(chan *tailer.LogLine)
	fs := afero.NewMemMapFs()
	o := LoaderOptions{store, inLines, w, fs, false, false, false, false, true, false}
	l, err := NewLoader(o)
	if err != nil {
		t.Fatalf("couldn't create loader: %s", err)
	}

	for _, f := range testProgFiles {
		afero.WriteFile(fs, f, []byte(testProgram), 0644)
		err = l.LoadProg(f)
		if err != nil {
			t.Fatalf("couldn't load file: %s error: %s", f, err)
		}
	}
}
<|endoftext|>


<commit_before>package httpclient

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httptrace"
	"net/http/httputil"
	"os"
	"strconv"
	"time"
)

// A transport that prints request and response

type LoggingTransport struct {
	t            http.RoundTripper
	requestBody  bool
	responseBody bool
	timing       bool
}

func (lt *LoggingTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	dreq, _ := httputil.DumpRequest(req, lt.requestBody)
	//fmt.Println("REQUEST:", strconv.Quote(string(dreq)))
	fmt.Println("URL:", req.URL)
	fmt.Println("REQUEST:", string(dreq))

	for _, t := range req.TransferEncoding {
		fmt.Println("Transfer-Encoding:", t)
	}

	fmt.Println("")

	var startTime time.Time
	var elapsed time.Duration

	if lt.timing {
		startTime = time.Now()
	}

	resp, err = lt.t.RoundTrip(req)

	if lt.timing {
		elapsed = time.Since(startTime)
	}

	if err != nil {
		if lt.requestBody {
			// don't print the body twice
			dreq, _ = httputil.DumpRequest(req, false)
		}
		fmt.Println("ERROR:", err, "REQUEST:", strconv.Quote(string(dreq)))
	}
	if resp != nil {
		dresp, _ := httputil.DumpResponse(resp, lt.responseBody)
		fmt.Println("RESPONSE:", string(dresp))

		for _, t := range resp.Request.TransferEncoding {
			fmt.Println("RQ Transfer-Encoding:", t)
		}
	}

	if elapsed > 0 {
		fmt.Println("ELAPSED TIME:", elapsed.Round(time.Millisecond))
	}

	fmt.Println("")
	return
}

// Enable logging requests/response headers
//
// if requestBody == true, also log request body
// if responseBody == true, also log response body
// if timing == true, also log elapsed time
func StartLogging(requestBody, responseBody, timing bool) {
	http.DefaultTransport = &LoggingTransport{&http.Transport{}, requestBody, responseBody, timing}

	DefaultTransport = &LoggingTransport{DefaultTransport, requestBody, responseBody, timing}
}

// Disable logging requests/responses
func StopLogging() {
	if lg, ok := http.DefaultTransport.(*LoggingTransport); ok {
		http.DefaultTransport = lg.t
	} else {
		http.DefaultTransport = &http.Transport{}
	}

	if lg, ok := DefaultTransport.(*LoggingTransport); ok {
		DefaultTransport = lg.t
	}
}

// Wrap input transport into a LoggingTransport
func LoggedTransport(t http.RoundTripper, requestBody, responseBody, timing bool) http.RoundTripper {
	return &LoggingTransport{t, requestBody, responseBody, timing}
}

// A Reader that "logs" progress

type ProgressReader struct {
	r         io.Reader
	c         [1]byte
	threshold int
	curr      int
}

func NewProgressReader(r io.Reader, c byte, threshold int) *ProgressReader {
	if c == 0 {
		c = '.'
	}
	if threshold <= 0 {
		threshold = 10240
	}
	p := &ProgressReader{r: r, c: [1]byte{c}, threshold: threshold, curr: 0}
	return p
}

func (p *ProgressReader) Read(b []byte) (int, error) {
	n, err := p.r.Read(b)

	p.curr += n

	if err == io.EOF {
		os.Stdout.Write([]byte{'\n'})
		os.Stdout.Sync()
	} else if p.curr >= p.threshold {
		p.curr -= p.threshold

		os.Stdout.Write(p.c[:])
		os.Stdout.Sync()
	}

	return n, err
}

func (p *ProgressReader) Close() error {
	if rc, ok := p.r.(io.ReadCloser); ok {
		return rc.Close()
	} else {
		return nil
	}
}

// A Logger that can be disabled

type DebugLog bool

func (d DebugLog) Println(args ...interface{}) {
	if d {
		log.Println(args...)
	}
}

func (d DebugLog) Printf(fmt string, args ...interface{}) {
	if d {
		log.Printf(fmt, args...)
	}
}

// A ClientTrace implementation that collects request time

type RequestTrace struct {
	DNS          time.Duration
	Connect      time.Duration
	Connected    bool
	TLSHandshake time.Duration
	Request      time.Duration
	Wait         time.Duration
	Response     time.Duration

	Local  string
	Remote string

	startTime time.Time
}

func (r *RequestTrace) Reset() {
	r.DNS = 0
	r.Connect = 0
	r.Connected = false
	r.TLSHandshake = 0
	r.Request = 0
	r.Wait = 0
	r.Response = 0
}

func (r *RequestTrace) NewClientTrace(trace bool) *httptrace.ClientTrace {
	return &httptrace.ClientTrace{
		GotConn: func(info httptrace.GotConnInfo) {
			r.Connected = info.WasIdle
			r.Local = info.Conn.LocalAddr().String()
			r.Remote = info.Conn.RemoteAddr().String()

			if trace {
				log.Println("GotConn", r.Remote, "reused:", info.Reused, "wasIdle:", info.WasIdle)
			}
		},

		ConnectStart: func(network, addr string) {
			r.startTime = time.Now()

			if trace {
				log.Println("ConnectStart", network, addr)
			}
		},

		ConnectDone: func(network, addr string, err error) {
			r.Connect = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("ConnectDone", network, addr, err, r.Connect)
			}
		},

		DNSStart: func(info httptrace.DNSStartInfo) {
			r.startTime = time.Now()

			if trace {
				log.Println("DNSStart", info.Host)
			}
		},

		DNSDone: func(info httptrace.DNSDoneInfo) {
			r.DNS = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("DNSDone", info.Addrs, r.DNS)
			}
		},

		TLSHandshakeStart: func() {
			r.startTime = time.Now()

			if trace {
				log.Println("TLSHandshakeStart")
			}
		},

		TLSHandshakeDone: func(state tls.ConnectionState, err error) {
			r.TLSHandshake = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("TLSHandshakeDone", err, r.TLSHandshake)
			}
		},

		WroteHeaderField: func(string, []string) {
			if r.startTime.IsZero() {
				r.startTime = time.Now()

				if trace {
					log.Println("WroteHeader")
				}
			}
		},

		WroteRequest: func(info httptrace.WroteRequestInfo) {
			r.Request = time.Since(r.startTime)
			r.startTime = time.Now()

			if trace {
				log.Println("WroteRequest", r.Request)
			}
		},

		GotFirstResponseByte: func() {
			r.Wait = time.Since(r.startTime)
			r.startTime = time.Now()

			if trace {
				log.Println("GotFirstResponseByte", r.Wait)
			}
		},
	}
}

// Call this after the response has been received to set the Response duration
// (there is no callback for this)

func (r *RequestTrace) Done() {
	r.Response = time.Since(r.startTime)
	r.startTime = time.Now()
}

func (r *RequestTrace) String() string {
	return fmt.Sprintf("{DNS:%v, Connect:%v, Connected:%v, TLSHandshake:%v, Request:%v, Wait:%v, Response:%v}",
		r.DNS.Truncate(100*time.Microsecond),
		r.Connect.Truncate(100*time.Microsecond),
		r.Connected,
		r.TLSHandshake.Truncate(100*time.Microsecond),
		r.Request.Truncate(100*time.Microsecond),
		r.Wait.Truncate(100*time.Microsecond),
		r.Response.Truncate(100*time.Microsecond))
}
<commit_msg>log messages via "log" package instead fmt (stder and timestamps)<commit_after>package httpclient

import (
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httptrace"
	"net/http/httputil"
	"os"
	"strconv"
	"time"
)

// A transport that prints request and response

type LoggingTransport struct {
	t            http.RoundTripper
	requestBody  bool
	responseBody bool
	timing       bool
}

func (lt *LoggingTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	dreq, _ := httputil.DumpRequest(req, lt.requestBody)
	//fmt.Println("REQUEST:", strconv.Quote(string(dreq)))
	log.Println("URL:", req.URL)
	log.Println("REQUEST:", string(dreq))

	for _, t := range req.TransferEncoding {
		log.Println("Transfer-Encoding:", t)
	}

	if req.Header.Get("Content-Length") == "" {
		log.Println("Content-Length:", req.ContentLength)
	} else {
		log.Println("Content-Length: from-headers")
	}

	log.Println("")

	var startTime time.Time
	var elapsed time.Duration

	if lt.timing {
		startTime = time.Now()
	}

	resp, err = lt.t.RoundTrip(req)

	if lt.timing {
		elapsed = time.Since(startTime)
	}

	if err != nil {
		if lt.requestBody {
			// don't print the body twice
			dreq, _ = httputil.DumpRequest(req, false)
		}
		log.Println("ERROR:", err, "REQUEST:", strconv.Quote(string(dreq)))
	}
	if resp != nil {
		dresp, _ := httputil.DumpResponse(resp, lt.responseBody)
		log.Println("RESPONSE:", string(dresp))

		for _, t := range resp.Request.TransferEncoding {
			log.Println("REQ Transfer-Encoding:", t)
		}
	}

	if elapsed > 0 {
		log.Println("ELAPSED TIME:", elapsed.Round(time.Millisecond))
	}

	log.Println("")
	return
}

// Enable logging requests/response headers
//
// if requestBody == true, also log request body
// if responseBody == true, also log response body
// if timing == true, also log elapsed time
func StartLogging(requestBody, responseBody, timing bool) {
	http.DefaultTransport = &LoggingTransport{&http.Transport{}, requestBody, responseBody, timing}

	DefaultTransport = &LoggingTransport{DefaultTransport, requestBody, responseBody, timing}
}

// Disable logging requests/responses
func StopLogging() {
	if lg, ok := http.DefaultTransport.(*LoggingTransport); ok {
		http.DefaultTransport = lg.t
	} else {
		http.DefaultTransport = &http.Transport{}
	}

	if lg, ok := DefaultTransport.(*LoggingTransport); ok {
		DefaultTransport = lg.t
	}
}

// Wrap input transport into a LoggingTransport
func LoggedTransport(t http.RoundTripper, requestBody, responseBody, timing bool) http.RoundTripper {
	return &LoggingTransport{t, requestBody, responseBody, timing}
}

// A Reader that "logs" progress

type ProgressReader struct {
	r         io.Reader
	c         [1]byte
	threshold int
	curr      int
}

func NewProgressReader(r io.Reader, c byte, threshold int) *ProgressReader {
	if c == 0 {
		c = '.'
	}
	if threshold <= 0 {
		threshold = 10240
	}
	p := &ProgressReader{r: r, c: [1]byte{c}, threshold: threshold, curr: 0}
	return p
}

func (p *ProgressReader) Read(b []byte) (int, error) {
	n, err := p.r.Read(b)

	p.curr += n

	if err == io.EOF {
		os.Stdout.Write([]byte{'\n'})
		os.Stdout.Sync()
	} else if p.curr >= p.threshold {
		p.curr -= p.threshold

		os.Stdout.Write(p.c[:])
		os.Stdout.Sync()
	}

	return n, err
}

func (p *ProgressReader) Close() error {
	if rc, ok := p.r.(io.ReadCloser); ok {
		return rc.Close()
	} else {
		return nil
	}
}

// A Logger that can be disabled

type DebugLog bool

func (d DebugLog) Println(args ...interface{}) {
	if d {
		log.Println(args...)
	}
}

func (d DebugLog) Printf(fmt string, args ...interface{}) {
	if d {
		log.Printf(fmt, args...)
	}
}

// A ClientTrace implementation that collects request time

type RequestTrace struct {
	DNS          time.Duration
	Connect      time.Duration
	Connected    bool
	TLSHandshake time.Duration
	Request      time.Duration
	Wait         time.Duration
	Response     time.Duration

	Local  string
	Remote string

	startTime time.Time
}

func (r *RequestTrace) Reset() {
	r.DNS = 0
	r.Connect = 0
	r.Connected = false
	r.TLSHandshake = 0
	r.Request = 0
	r.Wait = 0
	r.Response = 0
}

func (r *RequestTrace) NewClientTrace(trace bool) *httptrace.ClientTrace {
	return &httptrace.ClientTrace{
		GotConn: func(info httptrace.GotConnInfo) {
			r.Connected = info.WasIdle
			r.Local = info.Conn.LocalAddr().String()
			r.Remote = info.Conn.RemoteAddr().String()

			if trace {
				log.Println("GotConn", r.Remote, "reused:", info.Reused, "wasIdle:", info.WasIdle)
			}
		},

		ConnectStart: func(network, addr string) {
			r.startTime = time.Now()

			if trace {
				log.Println("ConnectStart", network, addr)
			}
		},

		ConnectDone: func(network, addr string, err error) {
			r.Connect = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("ConnectDone", network, addr, err, r.Connect)
			}
		},

		DNSStart: func(info httptrace.DNSStartInfo) {
			r.startTime = time.Now()

			if trace {
				log.Println("DNSStart", info.Host)
			}
		},

		DNSDone: func(info httptrace.DNSDoneInfo) {
			r.DNS = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("DNSDone", info.Addrs, r.DNS)
			}
		},

		TLSHandshakeStart: func() {
			r.startTime = time.Now()

			if trace {
				log.Println("TLSHandshakeStart")
			}
		},

		TLSHandshakeDone: func(state tls.ConnectionState, err error) {
			r.TLSHandshake = time.Since(r.startTime)
			r.startTime = time.Time{}

			if trace {
				log.Println("TLSHandshakeDone", err, r.TLSHandshake)
			}
		},

		WroteHeaderField: func(string, []string) {
			if r.startTime.IsZero() {
				r.startTime = time.Now()

				if trace {
					log.Println("WroteHeader")
				}
			}
		},

		WroteRequest: func(info httptrace.WroteRequestInfo) {
			r.Request = time.Since(r.startTime)
			r.startTime = time.Now()

			if trace {
				log.Println("WroteRequest", r.Request)
			}
		},

		GotFirstResponseByte: func() {
			r.Wait = time.Since(r.startTime)
			r.startTime = time.Now()

			if trace {
				log.Println("GotFirstResponseByte", r.Wait)
			}
		},
	}
}

// Call this after the response has been received to set the Response duration
// (there is no callback for this)

func (r *RequestTrace) Done() {
	r.Response = time.Since(r.startTime)
	r.startTime = time.Now()
}

func (r *RequestTrace) String() string {
	return fmt.Sprintf("{DNS:%v, Connect:%v, Connected:%v, TLSHandshake:%v, Request:%v, Wait:%v, Response:%v}",
		r.DNS.Truncate(100*time.Microsecond),
		r.Connect.Truncate(100*time.Microsecond),
		r.Connected,
		r.TLSHandshake.Truncate(100*time.Microsecond),
		r.Request.Truncate(100*time.Microsecond),
		r.Wait.Truncate(100*time.Microsecond),
		r.Response.Truncate(100*time.Microsecond))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"
)

type logEntry interface {
	fmt.Stringer
	eventType() string
}

type authAccepted bool

func (accepted authAccepted) String() string {
	if accepted {
		return "accepted"
	}
	return "rejected"
}

type authLog struct {
	User     string       `json:"user"`
	Accepted authAccepted `json:"accepted"`
}

type noAuthLog struct {
	authLog
}

func (entry noAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q without credentials %v", entry.User, entry.Accepted)
}
func (entry noAuthLog) eventType() string {
	return "no_auth"
}

type passwordAuthLog struct {
	authLog
	Password string `json:"password"`
}

func (entry passwordAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with password %q %v", entry.User, entry.Password, entry.Accepted)
}
func (entry passwordAuthLog) eventType() string {
	return "password_auth"
}

type publicKeyAuthLog struct {
	authLog
	PublicKeyFingerprint string `json:"public_key"`
}

func (entry publicKeyAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with public key %q %v", entry.User, entry.PublicKeyFingerprint, entry.Accepted)
}
func (entry publicKeyAuthLog) eventType() string {
	return "public_key_auth"
}

type keyboardInteractiveAuthLog struct {
	authLog
	Answers []string `json:"answers"`
}

func (entry keyboardInteractiveAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with keyboard interactive answers %q %v", entry.User, entry.Answers, entry.Accepted)
}
func (entry keyboardInteractiveAuthLog) eventType() string {
	return "keyboard_interactive_auth"
}

type connectionLog struct {
	ClientVersion string `json:"client_version"`
}

func (entry connectionLog) String() string {
	return fmt.Sprintf("connection with client version %q established", entry.ClientVersion)
}
func (entry connectionLog) eventType() string {
	return "connection"
}

type connectionCloseLog struct {
}

func (entry connectionCloseLog) String() string {
	return "connection closed"
}
func (entry connectionCloseLog) eventType() string {
	return "connection_close"
}

type tcpipForwardLog struct {
	Address string `json:"address"`
}

func (entry tcpipForwardLog) String() string {
	return fmt.Sprintf("TCP/IP forwarding on %v requested", entry.Address)
}
func (entry tcpipForwardLog) eventType() string {
	return "tcpip_forward"
}

type cancelTCPIPForwardLog struct {
	Address string `json:"address"`
}

func (entry cancelTCPIPForwardLog) String() string {
	return fmt.Sprintf("TCP/IP forwarding on %v canceled", entry.Address)
}
func (entry cancelTCPIPForwardLog) eventType() string {
	return "cancel_tcpip_forward"
}

type channelLog struct {
	ChannelID int `json:"channel_id"`
}

type sessionLog struct {
	channelLog
}

func (entry sessionLog) String() string {
	return fmt.Sprintf("[channel %04X] session requested", entry.ChannelID)
}
func (entry sessionLog) eventType() string {
	return "session"
}

type sessionCloseLog struct {
	channelLog
}

func (entry sessionCloseLog) String() string {
	return fmt.Sprintf("[channel %04X] closed", entry.ChannelID)
}
func (entry sessionCloseLog) eventType() string {
	return "session_close"
}

type sessionInputLog struct {
	channelLog
	Input string `json:"input"`
}

func (entry sessionInputLog) String() string {
	return fmt.Sprintf("[channel %04X] input: %q", entry.ChannelID, entry.Input)
}
func (entry sessionInputLog) eventType() string {
	return "session_input"
}

type directTCPIPLog struct {
	channelLog
	From string `json:"from"`
	To   string `json:"to"`
}

func (entry directTCPIPLog) String() string {
	return fmt.Sprintf("[channel %04X] direct TCP/IP forwarding from %v to %v requested", entry.ChannelID, entry.From, entry.To)
}
func (entry directTCPIPLog) eventType() string {
	return "direct_tcpip"
}

type directTCPIPCloseLog struct {
	channelLog
}

func (entry directTCPIPCloseLog) String() string {
	return fmt.Sprintf("[channel %04X] closed", entry.ChannelID)
}
func (entry directTCPIPCloseLog) eventType() string {
	return "direct_tcpip_close"
}

type directTCPIPInputLog struct {
	channelLog
	Input string `json:"input"`
}

func (entry directTCPIPInputLog) String() string {
	return fmt.Sprintf("[channel %04X] input: %q", entry.ChannelID, entry.Input)
}
func (entry directTCPIPInputLog) eventType() string {
	return "direct_tcpip_input"
}

type ptyLog struct {
	channelLog
	Terminal string `json:"terminal"`
	Width    uint32 `json:"width"`
	Height   uint32 `json:"height"`
}

func (entry ptyLog) String() string {
	return fmt.Sprintf("[channel %04X] PTY using terminal %q (size %vx%v) requested", entry.ChannelID, entry.Terminal, entry.Width, entry.Height)
}
func (entry ptyLog) eventType() string {
	return "pty"
}

type shellLog struct {
	channelLog
}

func (entry shellLog) String() string {
	return fmt.Sprintf("[channel %04X] shell requested", entry.ChannelID)
}
func (entry shellLog) eventType() string {
	return "shell"
}

type execLog struct {
	channelLog
	Command string `json:"command"`
}

func (entry execLog) String() string {
	return fmt.Sprintf("[channel %04X] command %q requested", entry.ChannelID, entry.Command)
}
func (entry execLog) eventType() string {
	return "exec"
}

type subsystemLog struct {
	channelLog
	Subsystem string `json:"subsystem"`
}

func (entry subsystemLog) String() string {
	return fmt.Sprintf("[channel %04X] subsystem %q requested", entry.ChannelID, entry.Subsystem)
}
func (entry subsystemLog) eventType() string {
	return "subsystem"
}

type x11Log struct {
	channelLog
	Screen uint32 `json:"screen"`
}

func (entry x11Log) String() string {
	return fmt.Sprintf("[channel %04X] X11 forwarding on screen %v requested", entry.ChannelID, entry.Screen)
}
func (entry x11Log) eventType() string {
	return "x11"
}

type envLog struct {
	channelLog
	Name  string `json:"name"`
	Value string `json:"value"`
}

func (entry envLog) String() string {
	return fmt.Sprintf("[channel %04X] environment variable %q with value %q requested", entry.ChannelID, entry.Name, entry.Value)
}
func (entry envLog) eventType() string {
	return "env"
}

type windowChangeLog struct {
	channelLog
	Width  uint32 `json:"width"`
	Height uint32 `json:"height"`
}

func (entry windowChangeLog) String() string {
	return fmt.Sprintf("[channel %04X] window size change to %vx%v requested", entry.ChannelID, entry.Width, entry.Height)
}
func (entry windowChangeLog) eventType() string {
	return "window_change"
}

func (metadata connMetadata) logEvent(entry logEntry) {
	if metadata.cfg.Logging.JSON {
		logBytes, err := json.Marshal(struct {
			Time      string   `json:"time"`
			Source    string   `json:"source"`
			EventType string   `json:"event_type"`
			Event     logEntry `json:"event"`
		}{time.Now().Format(time.RFC3339), metadata.RemoteAddr().String(), entry.eventType(), entry})
		if err != nil {
			warningLogger.Printf("Failed to log event: %v", err)
			return
		}
		log.Print(string(logBytes))
	} else {
		log.Printf("[%v] %v", metadata.RemoteAddr().String(), entry)
	}
}
<commit_msg>Log channel IDs in decimal without padding<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"
)

type logEntry interface {
	fmt.Stringer
	eventType() string
}

type authAccepted bool

func (accepted authAccepted) String() string {
	if accepted {
		return "accepted"
	}
	return "rejected"
}

type authLog struct {
	User     string       `json:"user"`
	Accepted authAccepted `json:"accepted"`
}

type noAuthLog struct {
	authLog
}

func (entry noAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q without credentials %v", entry.User, entry.Accepted)
}
func (entry noAuthLog) eventType() string {
	return "no_auth"
}

type passwordAuthLog struct {
	authLog
	Password string `json:"password"`
}

func (entry passwordAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with password %q %v", entry.User, entry.Password, entry.Accepted)
}
func (entry passwordAuthLog) eventType() string {
	return "password_auth"
}

type publicKeyAuthLog struct {
	authLog
	PublicKeyFingerprint string `json:"public_key"`
}

func (entry publicKeyAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with public key %q %v", entry.User, entry.PublicKeyFingerprint, entry.Accepted)
}
func (entry publicKeyAuthLog) eventType() string {
	return "public_key_auth"
}

type keyboardInteractiveAuthLog struct {
	authLog
	Answers []string `json:"answers"`
}

func (entry keyboardInteractiveAuthLog) String() string {
	return fmt.Sprintf("authentication for user %q with keyboard interactive answers %q %v", entry.User, entry.Answers, entry.Accepted)
}
func (entry keyboardInteractiveAuthLog) eventType() string {
	return "keyboard_interactive_auth"
}

type connectionLog struct {
	ClientVersion string `json:"client_version"`
}

func (entry connectionLog) String() string {
	return fmt.Sprintf("connection with client version %q established", entry.ClientVersion)
}
func (entry connectionLog) eventType() string {
	return "connection"
}

type connectionCloseLog struct {
}

func (entry connectionCloseLog) String() string {
	return "connection closed"
}
func (entry connectionCloseLog) eventType() string {
	return "connection_close"
}

type tcpipForwardLog struct {
	Address string `json:"address"`
}

func (entry tcpipForwardLog) String() string {
	return fmt.Sprintf("TCP/IP forwarding on %v requested", entry.Address)
}
func (entry tcpipForwardLog) eventType() string {
	return "tcpip_forward"
}

type cancelTCPIPForwardLog struct {
	Address string `json:"address"`
}

func (entry cancelTCPIPForwardLog) String() string {
	return fmt.Sprintf("TCP/IP forwarding on %v canceled", entry.Address)
}
func (entry cancelTCPIPForwardLog) eventType() string {
	return "cancel_tcpip_forward"
}

type channelLog struct {
	ChannelID int `json:"channel_id"`
}

type sessionLog struct {
	channelLog
}

func (entry sessionLog) String() string {
	return fmt.Sprintf("[channel %v] session requested", entry.ChannelID)
}
func (entry sessionLog) eventType() string {
	return "session"
}

type sessionCloseLog struct {
	channelLog
}

func (entry sessionCloseLog) String() string {
	return fmt.Sprintf("[channel %v] closed", entry.ChannelID)
}
func (entry sessionCloseLog) eventType() string {
	return "session_close"
}

type sessionInputLog struct {
	channelLog
	Input string `json:"input"`
}

func (entry sessionInputLog) String() string {
	return fmt.Sprintf("[channel %v] input: %q", entry.ChannelID, entry.Input)
}
func (entry sessionInputLog) eventType() string {
	return "session_input"
}

type directTCPIPLog struct {
	channelLog
	From string `json:"from"`
	To   string `json:"to"`
}

func (entry directTCPIPLog) String() string {
	return fmt.Sprintf("[channel %v] direct TCP/IP forwarding from %v to %v requested", entry.ChannelID, entry.From, entry.To)
}
func (entry directTCPIPLog) eventType() string {
	return "direct_tcpip"
}

type directTCPIPCloseLog struct {
	channelLog
}

func (entry directTCPIPCloseLog) String() string {
	return fmt.Sprintf("[channel %v] closed", entry.ChannelID)
}
func (entry directTCPIPCloseLog) eventType() string {
	return "direct_tcpip_close"
}

type directTCPIPInputLog struct {
	channelLog
	Input string `json:"input"`
}

func (entry directTCPIPInputLog) String() string {
	return fmt.Sprintf("[channel %v] input: %q", entry.ChannelID, entry.Input)
}
func (entry directTCPIPInputLog) eventType() string {
	return "direct_tcpip_input"
}

type ptyLog struct {
	channelLog
	Terminal string `json:"terminal"`
	Width    uint32 `json:"width"`
	Height   uint32 `json:"height"`
}

func (entry ptyLog) String() string {
	return fmt.Sprintf("[channel %v] PTY using terminal %q (size %vx%v) requested", entry.ChannelID, entry.Terminal, entry.Width, entry.Height)
}
func (entry ptyLog) eventType() string {
	return "pty"
}

type shellLog struct {
	channelLog
}

func (entry shellLog) String() string {
	return fmt.Sprintf("[channel %v] shell requested", entry.ChannelID)
}
func (entry shellLog) eventType() string {
	return "shell"
}

type execLog struct {
	channelLog
	Command string `json:"command"`
}

func (entry execLog) String() string {
	return fmt.Sprintf("[channel %v] command %q requested", entry.ChannelID, entry.Command)
}
func (entry execLog) eventType() string {
	return "exec"
}

type subsystemLog struct {
	channelLog
	Subsystem string `json:"subsystem"`
}

func (entry subsystemLog) String() string {
	return fmt.Sprintf("[channel %v] subsystem %q requested", entry.ChannelID, entry.Subsystem)
}
func (entry subsystemLog) eventType() string {
	return "subsystem"
}

type x11Log struct {
	channelLog
	Screen uint32 `json:"screen"`
}

func (entry x11Log) String() string {
	return fmt.Sprintf("[channel %v] X11 forwarding on screen %v requested", entry.ChannelID, entry.Screen)
}
func (entry x11Log) eventType() string {
	return "x11"
}

type envLog struct {
	channelLog
	Name  string `json:"name"`
	Value string `json:"value"`
}

func (entry envLog) String() string {
	return fmt.Sprintf("[channel %v] environment variable %q with value %q requested", entry.ChannelID, entry.Name, entry.Value)
}
func (entry envLog) eventType() string {
	return "env"
}

type windowChangeLog struct {
	channelLog
	Width  uint32 `json:"width"`
	Height uint32 `json:"height"`
}

func (entry windowChangeLog) String() string {
	return fmt.Sprintf("[channel %v] window size change to %vx%v requested", entry.ChannelID, entry.Width, entry.Height)
}
func (entry windowChangeLog) eventType() string {
	return "window_change"
}

func (metadata connMetadata) logEvent(entry logEntry) {
	if metadata.cfg.Logging.JSON {
		logBytes, err := json.Marshal(struct {
			Time      string   `json:"time"`
			Source    string   `json:"source"`
			EventType string   `json:"event_type"`
			Event     logEntry `json:"event"`
		}{time.Now().Format(time.RFC3339), metadata.RemoteAddr().String(), entry.eventType(), entry})
		if err != nil {
			warningLogger.Printf("Failed to log event: %v", err)
			return
		}
		log.Print(string(logBytes))
	} else {
		log.Printf("[%v] %v", metadata.RemoteAddr().String(), entry)
	}
}
<|endoftext|>