<commit_before>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package logger

import (
	"fmt"
	"github.com/cihub/seelog"
	"strings"
	"sync"
)

// The log messages will be "<tag><tagSuffix><message>"
const tagSuffix = ": "

// the tagname reserved to set the default level for unknown tags
const DefaultTag = "DEFAULT"

// the initial level for unknown tags
const DefaultLevel = "error"

// Holds a set of default values for future NewChannel calls
var levelMap = map[string]string{DefaultTag: DefaultLevel}

// simple ordering to allow <= to decide if log will be output
const (
	_ = iota
	traceValue
	debugValue
	infoValue
	warnValue
	errorValue
	criticalValue
	offValue
)

// This needs to correspond to seelog levels
var validLevels = map[string]int{
	"trace":    traceValue,
	"debug":    debugValue,
	"info":     infoValue,
	"warn":     warnValue,
	"error":    errorValue,
	"critical": criticalValue,
	"off":      offValue,
}

// The logging structure
// nice short name so use like:
//
//   var log *logger.L
//   log := logger.New("sometag")
type L struct {
	sync.Mutex
	tag          string
	formatPrefix string
	textPrefix   string
	level        string
	levelNumber  int
	log          seelog.LoggerInterface
}

// Pre-load default levels before creating any new logging channels
// invalid levels are simply skipped and repeated calls will
// accumulate new tag values and overwrite old tag values.
//
// This will not update currently open channels, it is only for new
// channels, it is intended to be called once after command-line
// arguments and any configuration files have been processed to
// establish logging defaults.
//
// the name "*" is reserved to set a level for any tags that do not
// have table entries.
func LoadLevels(levels map[string]string) {
	for tag, level := range levels {
		// make sure that levelMap only contains correct data
		// by ignoring invalid levels
		if _, ok := validLevels[level]; ok {
			levelMap[tag] = level
		}
	}
}

// Setup seelog to used a rotated log file and output all logs
// level control is now controlled by this module
func Initialise(file string, size int, number int) error {
	config := fmt.Sprintf(`
          <seelog type="adaptive"
                  mininterval="2000000"
                  maxinterval="100000000"
                  critmsgcount="500"
                  minlevel="trace">
              <outputs formatid="all">
                  <rollingfile type="size" filename="%s" maxsize="%d" maxrolls="%d" />
              </outputs>
              <formats>
                  <format id="all" format="%%Date %%Time [%%LEVEL] %%Msg%%n" />
              </formats>
          </seelog>`, file, size, number)

	logger, err := seelog.LoggerFromConfigAsString(config)
	if err != nil {
		return err
	}
	err = seelog.ReplaceLogger(logger)
	if nil == err {
		seelog.Current.Warn("LOGGER: ===== Logging system started =====")
	}
	return err
}

// flush all channels
func Finalise() {
	seelog.Current.Warn("LOGGER: ===== Logging system stopped =====")
	seelog.Flush()
}

// flush all channels
func Flush() {
	seelog.Flush()
}

// Open a new logging channel with a specified tag
func New(tag string) *L {

	// map % -> %% to be printf safe
	s := strings.Split(tag, "%")
	j := strings.Join(s, "%%")

	// determine the initial level
	level, ok := levelMap[tag]
	if !ok {
		level, ok = levelMap[DefaultTag]
	}
	if !ok {
		level = "error"
	}

	// create a logger channel
	return &L{
		tag:          tag, // for referencing default level
		formatPrefix: j + tagSuffix,
		textPrefix:   tag + tagSuffix,
		level:        level,
		levelNumber:  validLevels[level], // level is validated so get a non-zero value
		log:          seelog.Current,
	}
}

// Change the log level for a given channel returns the current level
//
// Use the value of DefaultTag to return to current default value.
// Use the value "" to just return the current setting
func (l *L) ChangeLevel(level string) string {
	// preserve current
	current := l.level

	// to return to default level which may have been modified
	// by subsequent LoadLevels calls.
	if DefaultTag == level {
		// get currrent default level for this tag
		var ok bool
		level, ok = levelMap[l.tag]
		if !ok {
			level, ok = levelMap[DefaultTag]
		}
		if !ok {
			level = DefaultLevel
		}
	} else if "" == level {
		return current
	}

	// set level and corresponding number
	if n, ok := validLevels[level]; ok {
		l.Lock()
		defer l.Unlock()
		l.level = level
		l.levelNumber = n
	}
	return current
}

// flush messages
func (l *L) Flush() {
	Flush()
}
<commit_msg>fix a comment<commit_after>// Copyright (c) 2014-2017 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package logger

import (
	"fmt"
	"github.com/cihub/seelog"
	"strings"
	"sync"
)

// The log messages will be "<tag><tagSuffix><message>"
const tagSuffix = ": "

// the tagname reserved to set the default level for unknown tags
const DefaultTag = "DEFAULT"

// the initial level for unknown tags
const DefaultLevel = "error"

// Holds a set of default values for future NewChannel calls
var levelMap = map[string]string{DefaultTag: DefaultLevel}

// simple ordering to allow <= to decide if log will be output
const (
	_ = iota
	traceValue
	debugValue
	infoValue
	warnValue
	errorValue
	criticalValue
	offValue
)

// This needs to correspond to seelog levels
var validLevels = map[string]int{
	"trace":    traceValue,
	"debug":    debugValue,
	"info":     infoValue,
	"warn":     warnValue,
	"error":    errorValue,
	"critical": criticalValue,
	"off":      offValue,
}

// The logging structure
// nice short name so use like:
//
//   var log *logger.L
//   log := logger.New("sometag")
type L struct {
	sync.Mutex
	tag          string
	formatPrefix string
	textPrefix   string
	level        string
	levelNumber  int
	log          seelog.LoggerInterface
}

// Pre-load default levels before creating any new logging channels
// invalid levels are simply skipped and repeated calls will
// accumulate new tag values and overwrite old tag values.
//
// This will not update currently open channels, it is only for new
// channels, it is intended to be called once after command-line
// arguments and any configuration files have been processed to
// establish logging defaults.
//
// the name from "DefaultTag" is reserved to set a level for any tags that do not
// have table entries.
func LoadLevels(levels map[string]string) {
	for tag, level := range levels {
		// make sure that levelMap only contains correct data
		// by ignoring invalid levels
		if _, ok := validLevels[level]; ok {
			levelMap[tag] = level
		}
	}
}

// Setup seelog to used a rotated log file and output all logs
// level control is now controlled by this module
func Initialise(file string, size int, number int) error {
	config := fmt.Sprintf(`
          <seelog type="adaptive"
                  mininterval="2000000"
                  maxinterval="100000000"
                  critmsgcount="500"
                  minlevel="trace">
              <outputs formatid="all">
                  <rollingfile type="size" filename="%s" maxsize="%d" maxrolls="%d" />
              </outputs>
              <formats>
                  <format id="all" format="%%Date %%Time [%%LEVEL] %%Msg%%n" />
              </formats>
          </seelog>`, file, size, number)

	logger, err := seelog.LoggerFromConfigAsString(config)
	if err != nil {
		return err
	}
	err = seelog.ReplaceLogger(logger)
	if nil == err {
		seelog.Current.Warn("LOGGER: ===== Logging system started =====")
	}
	return err
}

// flush all channels
func Finalise() {
	seelog.Current.Warn("LOGGER: ===== Logging system stopped =====")
	seelog.Flush()
}

// flush all channels
func Flush() {
	seelog.Flush()
}

// Open a new logging channel with a specified tag
func New(tag string) *L {

	// map % -> %% to be printf safe
	s := strings.Split(tag, "%")
	j := strings.Join(s, "%%")

	// determine the initial level
	level, ok := levelMap[tag]
	if !ok {
		level, ok = levelMap[DefaultTag]
	}
	if !ok {
		level = "error"
	}

	// create a logger channel
	return &L{
		tag:          tag, // for referencing default level
		formatPrefix: j + tagSuffix,
		textPrefix:   tag + tagSuffix,
		level:        level,
		levelNumber:  validLevels[level], // level is validated so get a non-zero value
		log:          seelog.Current,
	}
}

// Change the log level for a given channel returns the current level
//
// Use the value of DefaultTag to return to current default value.
// Use the value "" to just return the current setting
func (l *L) ChangeLevel(level string) string {
	// preserve current
	current := l.level

	// to return to default level which may have been modified
	// by subsequent LoadLevels calls.
	if DefaultTag == level {
		// get currrent default level for this tag
		var ok bool
		level, ok = levelMap[l.tag]
		if !ok {
			level, ok = levelMap[DefaultTag]
		}
		if !ok {
			level = DefaultLevel
		}
	} else if "" == level {
		return current
	}

	// set level and corresponding number
	if n, ok := validLevels[level]; ok {
		l.Lock()
		defer l.Unlock()
		l.level = level
		l.levelNumber = n
	}
	return current
}

// flush messages
func (l *L) Flush() {
	Flush()
}
<|endoftext|>


<commit_before>package console

import (
	"fmt"
	"github.com/mgutz/ansi"
	"path"
	"runtime"
	"strconv"
	"time"
)

//Logger options
type Options struct {
	Colors            ColorsOptions
	ContextMediumSize int
	SpaceSize         int
	DefaultTags       []string
}

type Show struct {
	Tags     bool
	Location bool
	Time     bool
}

//A log location
type Location struct {
	Filename string
	Line     int
}

//Logger struct stores log params
//up to the print
type Logger struct {
	Level          string
	Tags           []string
	Timestamp      int64
	ContextMessage string
	Message        string
	Hooks          internalHooks
	Show
	Location
	Options
}

//Reset the logger after print
func (logger *Logger) reset() {

	logger.Tags = []string{}
	logger.Show = Show{}

}

//Build the context string
//ie. tags, time, location
func (logger *Logger) buildContext() {

	var context string

	//Build location
	_, logger.Location.Filename, logger.Location.Line, _ = runtime.Caller(3)
	logger.Location.Filename = path.Base(logger.Location.Filename)

	//Build time
	logger.Timestamp = time.Now().Unix()

	//Build context message

	if logger.Show.Tags {
		for _, tag := range logger.Tags {
			context += "[" + tag + "]"
		}
	}

	if logger.Show.Location {
		context += " [" + logger.Location.Filename + ":" + strconv.Itoa(logger.Location.Line) + "] "
	}

	if logger.Show.Time {
		context += time.Now().Format(time.RFC3339) + " "
	}

	logger.ContextMessage = context
}

//Print the log in console
//And fire hooks
func (logger *Logger) PrintLog(color string, msg string, args ...interface{}) {

	if len(args) > 0 {
		logger.Message = fmt.Sprintf(msg, args...)
	} else {
		logger.Message = msg
	}

	//Build context string
	logger.buildContext()

	//Fire hook
	logger.Hooks.Fire(*logger)

	//Print message to console
	fmt.Println(ansi.Color(
		logger.ContextMessage+logger.Message,
		color,
	))

	logger.reset()
}

//Add tags to the log
func (logger *Logger) Tag(args ...string) *Logger {

	logger.Show.Tags = true
	logger.Tags = append(logger.Tags, args...)

	return logger
}

//Add file and line information to the log
func (logger *Logger) File() *Logger {

	logger.Show.Location = true

	return logger
}

//Add time information to the log
func (logger *Logger) Time() *Logger {
	logger.Show.Time = true

	return logger
}

//Log functions

//Log level
//	console.Log("Hello World")
func (logger *Logger) Log(msg string, args ...interface{}) {
	logger.Level = "log"
	color := logger.Options.Colors["Log"]
	logger.PrintLog(color, msg, args...)
}

//Info level
//	console.Info("Hello World")
func (logger *Logger) Info(msg string, args ...interface{}) {
	logger.Level = "info"
	color := logger.Options.Colors["Info"]
	logger.PrintLog(color, msg, args...)
}

//Error level
//	console.Error("Hello World")
func (logger *Logger) Error(msg string, args ...interface{}) {
	logger.Level = "error"
	color := logger.Options.Colors["Error"]
	logger.PrintLog(color, msg, args...)
}

//Warn level
//	console.Warn("Hello World")
func (logger *Logger) Warning(msg string, args ...interface{}) {
	logger.Level = "warning"
	color := logger.Options.Colors["Warning"]
	logger.PrintLog(color, msg, args...)
}
<commit_msg>Make PrintLog private<commit_after>package console

import (
	"fmt"
	"github.com/mgutz/ansi"
	"path"
	"runtime"
	"strconv"
	"time"
)

//Logger options
type Options struct {
	Colors            ColorsOptions
	ContextMediumSize int
	SpaceSize         int
	DefaultTags       []string
}

type Show struct {
	Tags     bool
	Location bool
	Time     bool
}

//A log location
type Location struct {
	Filename string
	Line     int
}

//Logger struct stores log params
//up to the print
type Logger struct {
	Level          string
	Tags           []string
	Timestamp      int64
	ContextMessage string
	Message        string
	Hooks          internalHooks
	Show
	Location
	Options
}

//Reset the logger after print
func (logger *Logger) reset() {

	logger.Tags = []string{}
	logger.Show = Show{}

}

//Build the context string
//ie. tags, time, location
func (logger *Logger) buildContext() {

	var context string

	//Build location
	_, logger.Location.Filename, logger.Location.Line, _ = runtime.Caller(3)
	logger.Location.Filename = path.Base(logger.Location.Filename)

	//Build time
	logger.Timestamp = time.Now().Unix()

	//Build context message

	if logger.Show.Tags {
		for _, tag := range logger.Tags {
			context += "[" + tag + "]"
		}
	}

	if logger.Show.Location {
		context += " [" + logger.Location.Filename + ":" + strconv.Itoa(logger.Location.Line) + "] "
	}

	if logger.Show.Time {
		context += time.Now().Format(time.RFC3339) + " "
	}

	logger.ContextMessage = context
}

//Print the log in console
//And fire hooks
func (logger *Logger) printLog(color string, msg string, args ...interface{}) {

	if len(args) > 0 {
		logger.Message = fmt.Sprintf(msg, args...)
	} else {
		logger.Message = msg
	}

	//Build context string
	logger.buildContext()

	//Fire hook
	logger.Hooks.Fire(*logger)

	//Print message to console
	fmt.Println(ansi.Color(
		logger.ContextMessage+logger.Message,
		color,
	))

	logger.reset()
}

//Add tags to the log
func (logger *Logger) Tag(args ...string) *Logger {

	logger.Show.Tags = true
	logger.Tags = append(logger.Tags, args...)

	return logger
}

//Add file and line information to the log
func (logger *Logger) File() *Logger {

	logger.Show.Location = true

	return logger
}

//Add time information to the log
func (logger *Logger) Time() *Logger {
	logger.Show.Time = true

	return logger
}

//Log functions

//Log level
//	console.Log("Hello World")
func (logger *Logger) Log(msg string, args ...interface{}) {
	logger.Level = "log"
	color := logger.Options.Colors["Log"]
	logger.printLog(color, msg, args...)
}

//Info level
//	console.Info("Hello World")
func (logger *Logger) Info(msg string, args ...interface{}) {
	logger.Level = "info"
	color := logger.Options.Colors["Info"]
	logger.printLog(color, msg, args...)
}

//Error level
//	console.Error("Hello World")
func (logger *Logger) Error(msg string, args ...interface{}) {
	logger.Level = "error"
	color := logger.Options.Colors["Error"]
	logger.printLog(color, msg, args...)
}

//Warn level
//	console.Warn("Hello World")
func (logger *Logger) Warning(msg string, args ...interface{}) {
	logger.Level = "warning"
	color := logger.Options.Colors["Warning"]
	logger.printLog(color, msg, args...)
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync/atomic"
)

// Level describes the chosen log level
type Level int

// LevelLogger represents levelled logger.
type LevelLogger struct {
	enabled int32
	level   Level
	prefix  string
	logger  *log.Logger
}

// Enabled exists to prevent calling underlying logger methods when not needed.
// This can also called from library users before calling LevelLogger methods
// to reduce allocations.
func (n *LevelLogger) Enabled() bool {
	return atomic.LoadInt32(&n.enabled) != 0
}

var callDepth = 2

// Print calls underlying Logger Print func.
func (n *LevelLogger) Print(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprint(v...))
}

// Printf calls underlying Logger Printf func.
func (n *LevelLogger) Printf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintf(format, v...))
}

// Println calls underlying Logger Println func.
func (n *LevelLogger) Println(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintln(v...))
}

// Fatal calls underlying Logger Fatal func.
func (n *LevelLogger) Fatal(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprint(v...))
	os.Exit(1)
}

// Fatalf calls underlying Logger Fatalf func.
func (n *LevelLogger) Fatalf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintf(format, v...))
	os.Exit(1)
}

// Fatalln calls underlying Logger Fatalln func.
func (n *LevelLogger) Fatalln(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintln(v...))
	os.Exit(1)
}

// Panic calls underlying Logger Panic func.
func (n *LevelLogger) Panic(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprint(v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

// Panicf calls underlying Logger Panicf func.
func (n *LevelLogger) Panicf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprintf(format, v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

// Panicln calls underlying Logger Panicln func.
func (n *LevelLogger) Panicln(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprintln(v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

const (
	LevelTrace Level = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelCritical
	LevelFatal
	LevelNone

	DefaultLogThreshold    = LevelInfo
	DefaultStdoutThreshold = LevelInfo
)

var (
	logger *log.Logger

	LogHandle  io.Writer = ioutil.Discard
	OutHandle  io.Writer = os.Stdout
	BothHandle io.Writer = io.MultiWriter(LogHandle, OutHandle)

	Flag int = log.Ldate | log.Ltime

	TRACE    *LevelLogger = &LevelLogger{level: LevelTrace, logger: logger, prefix: "[T]: "}
	DEBUG    *LevelLogger = &LevelLogger{level: LevelDebug, logger: logger, prefix: "[D]: "}
	INFO     *LevelLogger = &LevelLogger{level: LevelInfo, logger: logger, prefix: "[I]: "}
	WARN     *LevelLogger = &LevelLogger{level: LevelWarn, logger: logger, prefix: "[W]: "}
	ERROR    *LevelLogger = &LevelLogger{level: LevelError, logger: logger, prefix: "[E]: "}
	CRITICAL *LevelLogger = &LevelLogger{level: LevelCritical, logger: logger, prefix: "[C]: "}
	FATAL    *LevelLogger = &LevelLogger{level: LevelFatal, logger: logger, prefix: "[F]: "}

	loggers []*LevelLogger = []*LevelLogger{TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL, FATAL}

	logThreshold    Level = DefaultLogThreshold
	outputThreshold Level = DefaultStdoutThreshold
)

var LevelMatches = map[string]Level{
	"TRACE":    LevelTrace,
	"DEBUG":    LevelDebug,
	"INFO":     LevelInfo,
	"WARN":     LevelWarn,
	"ERROR":    LevelError,
	"CRITICAL": LevelCritical,
	"FATAL":    LevelFatal,
	"NONE":     LevelNone,
}

func init() {
	initialize()
}

// initialize initializes loggers.
func initialize() {
	BothHandle = io.MultiWriter(LogHandle, OutHandle)
	for _, l := range loggers {

		var handler io.Writer
		var enabled int32

		if l.level < outputThreshold && l.level < logThreshold {
			enabled = 0
			handler = ioutil.Discard
		} else if l.level >= outputThreshold && l.level >= logThreshold {
			enabled = 1
			handler = BothHandle
		} else if l.level >= outputThreshold && l.level < logThreshold {
			enabled = 1
			handler = OutHandle
		} else {
			enabled = 1
			handler = LogHandle
		}

		atomic.StoreInt32(&l.enabled, 0)
		l.logger = log.New(handler, l.prefix, Flag)
		atomic.StoreInt32(&l.enabled, enabled)
	}
}

// Ensures that the level provided is within the bounds of available levels.
func levelCheck(level Level) Level {
	switch {
	case level <= LevelTrace:
		return LevelTrace
	case level >= LevelFatal:
		return LevelFatal
	default:
		return level
	}
}

// SetLogThreshold establishes a threshold where anything matching or above will be logged.
func SetLogThreshold(level Level) {
	thresholdChanged := level != logThreshold
	if thresholdChanged {
		logThreshold = levelCheck(level)
		initialize()
	}
}

// SetStdoutThreshold establishes a threshold where anything matching or above will be output.
func SetStdoutThreshold(level Level) {
	thresholdChanged := level != outputThreshold
	if thresholdChanged {
		outputThreshold = levelCheck(level)
		initialize()
	}
}

// SetLogFile sets the LogHandle to a io.writer created for the file behind the given file path.
// Will append to this file.
func SetLogFile(path string) error {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	LogHandle = file
	initialize()
	return nil
}

// SetLogFlag sets global log flag used in package.
func SetLogFlag(flag int) {
	flagChanged := flag != Flag
	Flag = flag
	if flagChanged {
		initialize()
	}
}
<commit_msg>make LevelLogger implement io.Writer<commit_after>package logger

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"sync/atomic"
)

// Level describes the chosen log level
type Level int

// LevelLogger represents levelled logger.
type LevelLogger struct {
	enabled int32
	level   Level
	prefix  string
	logger  *log.Logger
}

// Enabled exists to prevent calling underlying logger methods when not needed.
// This can also called from library users before calling LevelLogger methods
// to reduce allocations.
func (n *LevelLogger) Enabled() bool {
	return atomic.LoadInt32(&n.enabled) != 0
}

var callDepth = 2

// Write allows LevelLogger to implement io.Writer interface so we can use it
// as output for other loggers.
func (n *LevelLogger) Write(p []byte) (int, error) {
	if !n.Enabled() {
		return len(p), nil
	}
	n.Printf("%s", p)
	return len(p), nil
}

// Print calls underlying Logger Print func.
func (n *LevelLogger) Print(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprint(v...))
}

// Printf calls underlying Logger Printf func.
func (n *LevelLogger) Printf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintf(format, v...))
}

// Println calls underlying Logger Println func.
func (n *LevelLogger) Println(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintln(v...))
}

// Fatal calls underlying Logger Fatal func.
func (n *LevelLogger) Fatal(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprint(v...))
	os.Exit(1)
}

// Fatalf calls underlying Logger Fatalf func.
func (n *LevelLogger) Fatalf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintf(format, v...))
	os.Exit(1)
}

// Fatalln calls underlying Logger Fatalln func.
func (n *LevelLogger) Fatalln(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	n.logger.Output(callDepth, fmt.Sprintln(v...))
	os.Exit(1)
}

// Panic calls underlying Logger Panic func.
func (n *LevelLogger) Panic(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprint(v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

// Panicf calls underlying Logger Panicf func.
func (n *LevelLogger) Panicf(format string, v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprintf(format, v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

// Panicln calls underlying Logger Panicln func.
func (n *LevelLogger) Panicln(v ...interface{}) {
	if !n.Enabled() {
		return
	}
	s := fmt.Sprintln(v...)
	n.logger.Output(callDepth, s)
	panic(s)
}

const (
	LevelTrace Level = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelCritical
	LevelFatal
	LevelNone

	DefaultLogThreshold    = LevelInfo
	DefaultStdoutThreshold = LevelInfo
)

var (
	logger *log.Logger

	LogHandle  io.Writer = ioutil.Discard
	OutHandle  io.Writer = os.Stdout
	BothHandle io.Writer = io.MultiWriter(LogHandle, OutHandle)

	Flag int = log.Ldate | log.Ltime

	TRACE    *LevelLogger = &LevelLogger{level: LevelTrace, logger: logger, prefix: "[T]: "}
	DEBUG    *LevelLogger = &LevelLogger{level: LevelDebug, logger: logger, prefix: "[D]: "}
	INFO     *LevelLogger = &LevelLogger{level: LevelInfo, logger: logger, prefix: "[I]: "}
	WARN     *LevelLogger = &LevelLogger{level: LevelWarn, logger: logger, prefix: "[W]: "}
	ERROR    *LevelLogger = &LevelLogger{level: LevelError, logger: logger, prefix: "[E]: "}
	CRITICAL *LevelLogger = &LevelLogger{level: LevelCritical, logger: logger, prefix: "[C]: "}
	FATAL    *LevelLogger = &LevelLogger{level: LevelFatal, logger: logger, prefix: "[F]: "}

	loggers []*LevelLogger = []*LevelLogger{TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL, FATAL}

	logThreshold    Level = DefaultLogThreshold
	outputThreshold Level = DefaultStdoutThreshold
)

var LevelMatches = map[string]Level{
	"TRACE":    LevelTrace,
	"DEBUG":    LevelDebug,
	"INFO":     LevelInfo,
	"WARN":     LevelWarn,
	"ERROR":    LevelError,
	"CRITICAL": LevelCritical,
	"FATAL":    LevelFatal,
	"NONE":     LevelNone,
}

func init() {
	initialize()
}

// initialize initializes loggers.
func initialize() {
	BothHandle = io.MultiWriter(LogHandle, OutHandle)
	for _, l := range loggers {

		var handler io.Writer
		var enabled int32

		if l.level < outputThreshold && l.level < logThreshold {
			enabled = 0
			handler = ioutil.Discard
		} else if l.level >= outputThreshold && l.level >= logThreshold {
			enabled = 1
			handler = BothHandle
		} else if l.level >= outputThreshold && l.level < logThreshold {
			enabled = 1
			handler = OutHandle
		} else {
			enabled = 1
			handler = LogHandle
		}

		atomic.StoreInt32(&l.enabled, 0)
		l.logger = log.New(handler, l.prefix, Flag)
		atomic.StoreInt32(&l.enabled, enabled)
	}
}

// Ensures that the level provided is within the bounds of available levels.
func levelCheck(level Level) Level {
	switch {
	case level <= LevelTrace:
		return LevelTrace
	case level >= LevelFatal:
		return LevelFatal
	default:
		return level
	}
}

// SetLogThreshold establishes a threshold where anything matching or above will be logged.
func SetLogThreshold(level Level) {
	thresholdChanged := level != logThreshold
	if thresholdChanged {
		logThreshold = levelCheck(level)
		initialize()
	}
}

// SetStdoutThreshold establishes a threshold where anything matching or above will be output.
func SetStdoutThreshold(level Level) {
	thresholdChanged := level != outputThreshold
	if thresholdChanged {
		outputThreshold = levelCheck(level)
		initialize()
	}
}

// SetLogFile sets the LogHandle to a io.writer created for the file behind the given file path.
// Will append to this file.
func SetLogFile(path string) error {
	file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	LogHandle = file
	initialize()
	return nil
}

// SetLogFlag sets global log flag used in package.
func SetLogFlag(flag int) {
	flagChanged := flag != Flag
	Flag = flag
	if flagChanged {
		initialize()
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-logger Authors. All rights reserved.
// This code is MIT licensed. See the LICENSE file for more info.

// Package logger is a better logging system for Go than the generic log
// package in the Go Standard Library. The logger packages provides colored
// output, logging levels, custom log formatting, and simultaneous logging
// output stream to stdout, stderr, and os.File.
package logger

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"text/template"
	"time"
)

// Used for string output of the logging object
var levels = [5]string{
	"DEBUG",
	"INFO",
	"WARNING",
	"ERROR",
	"CRITICAL",
}

type level int

// Returns the string representation of the level
func (l level) String() string { return levels[l] }

// The DEBUG level is the lowest possible output level. This is meant for
// development use. The default output level is WARNING.
const (
	// DEBUG level messages should be used for development logging instead
	// of Printf calls. When used in this manner, instead of sprinkling
	// Printf calls everywhere and then having to remove them once the bug
	// is fixed, the developer can simply change to a higher logging level
	// and the debug messages will not be sent to the output stream.
	DEBUG level = iota
	// Info level messages should be used to convey more informative output
	// than debug that could be used by a user.
	INFO
	// Warning messages should be used to notify the user that something
	// worked, but the expected value was not the result.
	WARNING
	// Error messages should be used when something just did not work at
	// all.
	ERROR
	// Critical messages are used when something is completely broken and
	// unrecoverable. Critical messages are usually followed by os.Exit().
	CRITICAL
)

// These flags define which text to prefix to each log entry generated by the Logger.
const (
	// Bits or'ed together to control what's printed.
	Ldate = 1 << iota
	// full file name and line number: /a/b/c/d.go:23
	Llongfile
	// base file name and line number: d.go:23. overrides Llongfile
	Lshortfile
	// Use ansi escape sequences
	Lansi
	// initial values for the standard logger
	LstdFlags = Ldate | Lansi
)

var (
	defPrefix      = ">>>"
	defColorPrefix = AnsiEscape(BOLD, GREEN, ">>>", OFF)
	// std is the default logger object
	log = New(os.Stderr, WARNING)
)

// A Logger represents an active logging object that generates lines of output
// to an io.Writer. Each logging operation makes a single call to the Writer's
// Write method. A Logger can be used simultaneously from multiple goroutines;
// it guarantees to serialize access to the Writer.
type Logger struct {
	mu         sync.Mutex         // Ensures atomic writes
	buf        []byte             // For marshaling output to write
	Colors     bool               // Enable/Disable colored output
	DateFormat string             // time.RubyDate is the default format
	Flags      int                // Properties of the output
	Level      level              // The default level is warning
	Template   *template.Template // The format order of the output
	Prefix     string             // Inserted into every logging output
	Stream     io.Writer          // Destination for output
}

// formatOutput is used by Output() to apply the desired output format using
// the logTemplate. Using this template, an output string is built containing
// the desired structure such as prefix, date, and file + line number.
func (l *Logger) formatOutput(buf *[]byte, t time.Time, file string,
	line int, text string) {
	l.buf = append(l.buf, t.Format(l.dateFormat)...)
	if len(text) > 0 && text[len(text)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}
}

// Output is used by all of the logging functions to send output to the output
// stream.
//
// calldepth is the number of stack frames to skip when getting the file
// name of original calling function for file name output.
//
// text is the string to append to the assembled log format output.
//
// stream will be used as the output stream the text will be written to. If
// stream is nil, the stream value contained in the logger object is used.
func (l *Logger) Fprint(calldepth int,
	text string, stream io.Writer) (n int, err error) {
	now := time.Now()
	var file string
	var line int
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.Flags&(Lshortfile|Llongfile) != 0 {
		// release lock while getting caller info - it's expensive.
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	l.formatOutput(&l.buf, now, file, line, text)
	if stream == nil {
		n, err = l.Stream.Write(l.buf)
	} else {
		n, err = stream.Write(l.buf)
	}
	return int(n), err
}

// Print sends output to the standard logger output stream regardless of
// logging level including the logger format properties and flags. Spaces are
// added between operands when neither is a string. It returns the number of
// bytes written and any write error encountered.
func (l *Logger) Print(v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprint(v...), os.Stdout)
}

// Println formats using the default formats for its operands and writes to
// standard output. Spaces are always added between operands and a newline is
// appended. It returns the number of bytes written and any write error
// encountered.
func (l *Logger) Println(v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprintln(v...), os.Stdout)
}

// Printf formats according to a format specifier and writes to standard
// output. It returns the number of bytes written and any write error
// encountered.
func (l *Logger) Printf(format string, v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprintf(format, v...), os.Stdout)
}

// New creates a new logger object and returns it.
func New(stream io.Writer, level level) (obj *Logger) {
	tmpl := template.Must(template.New("std").Funcs(funcMap).Parse(logFmt))
	obj = &Logger{Stream: stream, Colors: true, DateFormat: time.RubyDate,
		Flags: LstdFlags, Level: level, Template: tmpl,
		Prefix: defColorPrefix}
	return
}
<commit_msg>Remove formatOutput()<commit_after>// Copyright 2013 The go-logger Authors. All rights reserved.
// This code is MIT licensed. See the LICENSE file for more info.

// Package logger is a better logging system for Go than the generic log
// package in the Go Standard Library. The logger packages provides colored
// output, logging levels, custom log formatting, and simultaneous logging
// output stream to stdout, stderr, and os.File.
package logger

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"text/template"
	"time"
)

// Used for string output of the logging object
var levels = [5]string{
	"DEBUG",
	"INFO",
	"WARNING",
	"ERROR",
	"CRITICAL",
}

type level int

// Returns the string representation of the level
func (l level) String() string { return levels[l] }

// The DEBUG level is the lowest possible output level. This is meant for
// development use. The default output level is WARNING.
const (
	// DEBUG level messages should be used for development logging instead
	// of Printf calls. When used in this manner, instead of sprinkling
	// Printf calls everywhere and then having to remove them once the bug
	// is fixed, the developer can simply change to a higher logging level
	// and the debug messages will not be sent to the output stream.
	DEBUG level = iota
	// Info level messages should be used to convey more informative output
	// than debug that could be used by a user.
	INFO
	// Warning messages should be used to notify the user that something
	// worked, but the expected value was not the result.
	WARNING
	// Error messages should be used when something just did not work at
	// all.
	ERROR
	// Critical messages are used when something is completely broken and
	// unrecoverable. Critical messages are usually followed by os.Exit().
	CRITICAL
)

// These flags define which text to prefix to each log entry generated by the Logger.
const (
	// Bits or'ed together to control what's printed.
	Ldate = 1 << iota
	// full file name and line number: /a/b/c/d.go:23
	Llongfile
	// base file name and line number: d.go:23. overrides Llongfile
	Lshortfile
	// Use ansi escape sequences
	Lansi
	// initial values for the standard logger
	LstdFlags = Ldate | Lansi
)

var (
	defPrefix      = ">>>"
	defColorPrefix = AnsiEscape(BOLD, GREEN, ">>>", OFF)
	// std is the default logger object
	log = New(os.Stderr, WARNING)
)

// A Logger represents an active logging object that generates lines of output
// to an io.Writer. Each logging operation makes a single call to the Writer's
// Write method. A Logger can be used simultaneously from multiple goroutines;
// it guarantees to serialize access to the Writer.
type Logger struct {
	mu         sync.Mutex         // Ensures atomic writes
	buf        []byte             // For marshaling output to write
	Colors     bool               // Enable/Disable colored output
	DateFormat string             // time.RubyDate is the default format
	Flags      int                // Properties of the output
	Level      level              // The default level is warning
	Template   *template.Template // The format order of the output
	Prefix     string             // Inserted into every logging output
	Stream     io.Writer          // Destination for output
}

// Output is used by all of the logging functions to send output to the output
// stream.
//
// calldepth is the number of stack frames to skip when getting the file
// name of original calling function for file name output.
//
// text is the string to append to the assembled log format output.
//
// stream will be used as the output stream the text will be written to. If
// stream is nil, the stream value contained in the logger object is used.
func (l *Logger) Fprint(calldepth int,
	text string, stream io.Writer) (n int, err error) {
	now := time.Now()
	var file string
	var line int
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.Flags&(Lshortfile|Llongfile) != 0 {
		// release lock while getting caller info - it's expensive.
		l.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		l.mu.Lock()
	}
	l.buf = l.buf[:0]
	if stream == nil {
		n, err = l.Stream.Write(l.buf)
	} else {
		n, err = stream.Write(l.buf)
	}
	return int(n), err
}

// Print sends output to the standard logger output stream regardless of
// logging level including the logger format properties and flags. Spaces are
// added between operands when neither is a string. It returns the number of
// bytes written and any write error encountered.
func (l *Logger) Print(v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprint(v...), os.Stdout)
}

// Println formats using the default formats for its operands and writes to
// standard output. Spaces are always added between operands and a newline is
// appended. It returns the number of bytes written and any write error
// encountered.
func (l *Logger) Println(v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprintln(v...), os.Stdout)
}

// Printf formats according to a format specifier and writes to standard
// output. It returns the number of bytes written and any write error
// encountered.
func (l *Logger) Printf(format string, v ...interface{}) (n int, err error) {
	return l.Fprint(2, fmt.Sprintf(format, v...), os.Stdout)
}

// New creates a new logger object and returns it.
func New(stream io.Writer, level level) (obj *Logger) {
	tmpl := template.Must(template.New("std").Funcs(funcMap).Parse(logFmt))
	obj = &Logger{Stream: stream, Colors: true, DateFormat: time.RubyDate,
		Flags: LstdFlags, Level: level, Template: tmpl,
		Prefix: defColorPrefix}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"net"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "bind"
)

var (
	up = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "up"),
		"Was the Bind instance query successful?",
		nil, nil,
	)
	incomingQueries = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "incoming_queries_total"),
		"Number of incomming DNS queries.",
		[]string{"name"}, nil,
	)
	incomingRequests = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "incoming_requests_total"),
		"Number of incomming DNS queries.",
		[]string{"name"}, nil,
	)
	resolverQueries = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "resolver_queries_total"),
		"Number of outgoing DNS queries.",
		[]string{"view", "name"}, nil,
	)
	resolverQueryDuration = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "resolver_query_duration_seconds"),
		"Resolver query round-trip time in seconds.",
		[]string{"view"}, nil,
	)
)

// Exporter collects Binds stats from the given server and exports
// them using the prometheus metrics package.
type Exporter struct {
	URI    string
	client *http.Client
}

// NewExporter returns an initialized Exporter.
func NewExporter(uri string, timeout time.Duration) *Exporter {
	return &Exporter{
		URI: uri,
		client: &http.Client{
			Transport: &http.Transport{
				Dial: func(netw, addr string) (net.Conn, error) {
					c, err := net.DialTimeout(netw, addr, timeout)
					if err != nil {
						return nil, err
					}
					if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
						return nil, err
					}
					return c, nil
				},
			},
		},
	}
}

// Describe describes all the metrics ever exported by the bind
// exporter. It implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- up
	ch <- incomingQueries
	ch <- incomingRequests
	ch <- resolverQueries
	ch <- resolverQueryDuration
}

// Collect fetches the stats from configured bind location and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	var status float64
	defer func() {
		ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, status)
	}()

	resp, err := e.client.Get(e.URI)
	if err != nil {
		log.Error("Error while querying Bind: ", err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Error("Failed to read XML response body: ", err)
		return
	}

	status = 1

	root := Isc{}
	if err := xml.Unmarshal([]byte(body), &root); err != nil {
		log.Error("Failed to unmarshal XML response: ", err)
		return
	}

	serverNode := root.Bind.Statistics.Server
	for _, s := range serverNode.QueriesIn.Rdtype {
		ch <- prometheus.MustNewConstMetric(
			incomingQueries, prometheus.CounterValue, float64(s.Counter), s.Name,
		)
	}
	for _, s := range serverNode.Requests.Opcode {
		ch <- prometheus.MustNewConstMetric(
			incomingRequests, prometheus.CounterValue, float64(s.Counter), s.Name,
		)
	}

	for _, v := range root.Bind.Statistics.Views {
		for _, s := range v.Rdtype {
			ch <- prometheus.MustNewConstMetric(
				resolverQueries, prometheus.CounterValue, float64(s.Counter), v.Name, s.Name,
			)
		}

		if buckets, count, err := histogram(v.Resstat); err == nil {
			ch <- prometheus.MustNewConstHistogram(
				resolverQueryDuration, count, math.NaN(), buckets, v.Name,
			)
		} else {
			log.Warn("Error parsing RTT:", err)
		}
	}
}

func histogram(stats []Stat) (map[float64]uint64, uint64, error) {
	buckets := map[float64]uint64{}
	var count uint64

	for _, s := range stats {
		if strings.HasPrefix(s.Name, qryRTT) {
			b := math.Inf(0)
			if !strings.HasSuffix(s.Name, "+") {
				var err error
				rrt := strings.TrimPrefix(s.Name, qryRTT)
				b, err = strconv.ParseFloat(rrt, 32)
				if err != nil {
					return buckets, 0, fmt.Errorf("could not parse RTT: %s", rrt)
				}
			}

			buckets[b/1000] = count + uint64(s.Counter)
			count += uint64(s.Counter)
		}
	}
	return buckets, count, nil
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9109", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		bindURI       = flag.String("bind.statsuri", "http://localhost:8053/", "HTTP XML API address of an Bind server.")
		bindTimeout   = flag.Duration("bind.timeout", 10*time.Second, "Timeout for trying to get stats from Bind.")
		bindPidFile   = flag.String("bind.pid-file", "", "Path to Bind's pid file to export process information.")
	)
	flag.Parse()

	prometheus.MustRegister(NewExporter(*bindURI, *bindTimeout))
	if *bindPidFile != "" {
		procExporter := prometheus.NewProcessCollectorPIDFn(
			func() (int, error) {
				content, err := ioutil.ReadFile(*bindPidFile)
				if err != nil {
					return 0, fmt.Errorf("Can't read pid file: %s", err)
				}
				value, err := strconv.Atoi(strings.TrimSpace(string(content)))
				if err != nil {
					return 0, fmt.Errorf("Can't parse pid file: %s", err)
				}
				return value, nil
			}, namespace)
		prometheus.MustRegister(procExporter)
	}

	log.Info("Starting Server: ", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Bind Exporter</title>

Bind Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Add majority of resolver statistics<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"math"
	"net"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
)

const (
	namespace = "bind"
	resolver  = "resolver"
)

var (
	up = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "up"),
		"Was the Bind instance query successful?",
		nil, nil,
	)
	incomingQueries = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "incoming_queries_total"),
		"Number of incomming DNS queries.",
		[]string{"name"}, nil,
	)
	incomingRequests = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "", "incoming_requests_total"),
		"Number of incomming DNS queries.",
		[]string{"name"}, nil,
	)
	resolverQueries = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, resolver, "queries_total"),
		"Number of outgoing DNS queries.",
		[]string{"view", "name"}, nil,
	)
	resolverQueryDuration = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, resolver, "query_duration_seconds"),
		"Resolver query round-trip time in seconds.",
		[]string{"view"}, nil,
	)
	resolverQueryErrors = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, resolver, "query_errors_total"),
		"Number of resolver queries failed.",
		[]string{"view", "error"}, nil,
	)
	resolverResponseErrors = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, resolver, "response_errors_total"),
		"Number of resolver reponse errors received.",
		[]string{"view", "error"}, nil,
	)
	resolverDNSSECSucess = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, resolver, "dnssec_validation_success_total"),
		"Number of DNSSEC validation attempts succeeded.",
		[]string{"view", "result"}, nil,
	)
	resolverMetricStats = map[string]*prometheus.Desc{
		"Lame": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "response_lame_total"),
			"Number of lame delegation responses received.",
			[]string{"view"}, nil,
		),
		"EDNS0Fail": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "query_edns0_errors_total"),
			"Number of EDNS(0) query errors.",
			[]string{"view"}, nil,
		),
		"Mismatch": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "response_mismatch_total"),
			"Number of mismatch responses received.",
			[]string{"view"}, nil,
		),
		"Retry": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "query_retries_total"),
			"Number of resolver query retries.",
			[]string{"view"}, nil,
		),
		"Truncated": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "response_truncated_total"),
			"Number of truncated responses received.",
			[]string{"view"}, nil,
		),
		"ValFail": prometheus.NewDesc(
			prometheus.BuildFQName(namespace, resolver, "dnssec_validation_errors_total"),
			"Number of DNSSEC validation attempt errors.",
			[]string{"view"}, nil,
		),
	}
	resolverLabelStats = map[string]*prometheus.Desc{
		"QueryAbort":    resolverQueryErrors,
		"QuerySockFail": resolverQueryErrors,
		"QueryTimeout":  resolverQueryErrors,
		"NXDOMAIN":      resolverResponseErrors,
		"SERVFAIL":      resolverResponseErrors,
		"FORMERR":       resolverResponseErrors,
		"OtherError":    resolverResponseErrors,
		"ValOk":         resolverDNSSECSucess,
		"ValNegOk":      resolverDNSSECSucess,
	}
)

// Exporter collects Binds stats from the given server and exports
// them using the prometheus metrics package.
type Exporter struct {
	URI    string
	client *http.Client
}

// NewExporter returns an initialized Exporter.
func NewExporter(uri string, timeout time.Duration) *Exporter {
	return &Exporter{
		URI: uri,
		client: &http.Client{
			Transport: &http.Transport{
				Dial: func(netw, addr string) (net.Conn, error) {
					c, err := net.DialTimeout(netw, addr, timeout)
					if err != nil {
						return nil, err
					}
					if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
						return nil, err
					}
					return c, nil
				},
			},
		},
	}
}

// Describe describes all the metrics ever exported by the bind
// exporter. It implements prometheus.Collector.
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- up
	ch <- incomingQueries
	ch <- incomingRequests
	ch <- resolverDNSSECSucess
	ch <- resolverQueries
	ch <- resolverQueryDuration
	ch <- resolverQueryErrors
	ch <- resolverResponseErrors
	for _, desc := range resolverMetricStats {
		ch <- desc
	}
}

// Collect fetches the stats from configured bind location and
// delivers them as Prometheus metrics. It implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
	var status float64
	defer func() {
		ch <- prometheus.MustNewConstMetric(up, prometheus.GaugeValue, status)
	}()

	resp, err := e.client.Get(e.URI)
	if err != nil {
		log.Error("Error while querying Bind: ", err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Error("Failed to read XML response body: ", err)
		return
	}

	status = 1

	root := Isc{}
	if err := xml.Unmarshal([]byte(body), &root); err != nil {
		log.Error("Failed to unmarshal XML response: ", err)
		return
	}

	serverNode := root.Bind.Statistics.Server
	for _, s := range serverNode.QueriesIn.Rdtype {
		ch <- prometheus.MustNewConstMetric(
			incomingQueries, prometheus.CounterValue, float64(s.Counter), s.Name,
		)
	}
	for _, s := range serverNode.Requests.Opcode {
		ch <- prometheus.MustNewConstMetric(
			incomingRequests, prometheus.CounterValue, float64(s.Counter), s.Name,
		)
	}

	for _, v := range root.Bind.Statistics.Views {
		for _, s := range v.Rdtype {
			ch <- prometheus.MustNewConstMetric(
				resolverQueries, prometheus.CounterValue, float64(s.Counter), v.Name, s.Name,
			)
		}

		for _, s := range v.Resstat {
			if desc, ok := resolverMetricStats[s.Name]; ok {
				ch <- prometheus.MustNewConstMetric(
					desc, prometheus.CounterValue, float64(s.Counter), v.Name,
				)
			}
			if desc, ok := resolverLabelStats[s.Name]; ok {
				ch <- prometheus.MustNewConstMetric(
					desc, prometheus.CounterValue, float64(s.Counter), v.Name, s.Name,
				)
			}
		}

		if buckets, count, err := histogram(v.Resstat); err == nil {
			ch <- prometheus.MustNewConstHistogram(
				resolverQueryDuration, count, math.NaN(), buckets, v.Name,
			)
		} else {
			log.Warn("Error parsing RTT:", err)
		}
	}
}

func histogram(stats []Stat) (map[float64]uint64, uint64, error) {
	buckets := map[float64]uint64{}
	var count uint64

	for _, s := range stats {
		if strings.HasPrefix(s.Name, qryRTT) {
			b := math.Inf(0)
			if !strings.HasSuffix(s.Name, "+") {
				var err error
				rrt := strings.TrimPrefix(s.Name, qryRTT)
				b, err = strconv.ParseFloat(rrt, 32)
				if err != nil {
					return buckets, 0, fmt.Errorf("could not parse RTT: %s", rrt)
				}
			}

			buckets[b/1000] = count + uint64(s.Counter)
			count += uint64(s.Counter)
		}
	}
	return buckets, count, nil
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9109", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		bindURI       = flag.String("bind.statsuri", "http://localhost:8053/", "HTTP XML API address of an Bind server.")
		bindTimeout   = flag.Duration("bind.timeout", 10*time.Second, "Timeout for trying to get stats from Bind.")
		bindPidFile   = flag.String("bind.pid-file", "", "Path to Bind's pid file to export process information.")
	)
	flag.Parse()

	prometheus.MustRegister(NewExporter(*bindURI, *bindTimeout))
	if *bindPidFile != "" {
		procExporter := prometheus.NewProcessCollectorPIDFn(
			func() (int, error) {
				content, err := ioutil.ReadFile(*bindPidFile)
				if err != nil {
					return 0, fmt.Errorf("Can't read pid file: %s", err)
				}
				value, err := strconv.Atoi(strings.TrimSpace(string(content)))
				if err != nil {
					return 0, fmt.Errorf("Can't parse pid file: %s", err)
				}
				return value, nil
			}, namespace)
		prometheus.MustRegister(procExporter)
	}

	log.Info("Starting Server: ", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Bind Exporter</title>

Bind Exporter

<a href='` + *metricsPath + `'>Metrics

             `))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package bitmap

import (
	"github.com/RoaringBitmap/roaring"

	"github.com/anacrolix/missinggo"
)

// Bitmaps store the existence of values in [0,math.MaxUint32] more
// efficiently than []bool. The empty value starts with no bits set.
type Bitmap struct {
	rb *roaring.Bitmap
}

func (me *Bitmap) Len() int {
	if me.rb == nil {
		return 0
	}
	return int(me.rb.GetCardinality())
}

func (me Bitmap) ToSortedSlice() (ret []int) {
	if me.rb == nil {
		return
	}
	missinggo.CastSlice(&ret, me.rb.ToArray())
	return
}

func (me *Bitmap) lazyRB() *roaring.Bitmap {
	if me.rb == nil {
		me.rb = roaring.NewBitmap()
	}
	return me.rb
}

func (me *Bitmap) Iter(f func(interface{}) bool) {
	me.IterTyped(func(i int) bool {
		return f(i)
	})
}

func (me Bitmap) IterTyped(f func(int) bool) bool {
	if me.rb == nil {
		return true
	}
	it := me.rb.Iterator()
	for it.HasNext() {
		if !f(int(it.Next())) {
			return false
		}
	}
	return true
}

func (me *Bitmap) Add(is ...int) {
	rb := me.lazyRB()
	for _, i := range is {
		rb.AddInt(i)
	}
}

func (me *Bitmap) AddRange(begin, end int) {
	if begin >= end {
		return
	}
	me.lazyRB().AddRange(uint32(begin), uint32(end))
}

func (me *Bitmap) Remove(i int) {
	if me.rb == nil {
		return
	}
	me.rb.Remove(uint32(i))
}

func (me *Bitmap) Union(other *Bitmap) {
	me.lazyRB().Or(other.lazyRB())
}

func (me *Bitmap) Contains(i int) bool {
	if me.rb == nil {
		return false
	}
	return me.rb.Contains(uint32(i))
}

type Iter struct {
	ii roaring.IntIterable
}

func (me *Iter) Next() bool {
	if me == nil {
		return false
	}
	return me.ii.HasNext()
}

func (me *Iter) Value() interface{} {
	return me.ValueInt()
}

func (me *Iter) ValueInt() int {
	return int(me.ii.Next())
}

func (me *Iter) Stop() {}

func Sub(left, right *Bitmap) *Bitmap {
	return &Bitmap{
		rb: roaring.AndNot(left.lazyRB(), right.lazyRB()),
	}
}

func (me *Bitmap) Sub(other *Bitmap) {
	if other.rb == nil {
		return
	}
	if me.rb == nil {
		return
	}
	me.rb.AndNot(other.rb)
}

func (me *Bitmap) Clear() {
	if me.rb == nil {
		return
	}
	me.rb.Clear()
}

func (me Bitmap) Copy() (ret Bitmap) {
	ret = me
	if ret.rb != nil {
		ret.rb = ret.rb.Clone()
	}
	return
}

func (me *Bitmap) FlipRange(begin, end int) {
	me.lazyRB().FlipInt(begin, end)
}
<commit_msg>bitmap.Bitmap: Get and Set<commit_after>package bitmap

import (
	"github.com/RoaringBitmap/roaring"

	"github.com/anacrolix/missinggo"
)

// Bitmaps store the existence of values in [0,math.MaxUint32] more
// efficiently than []bool. The empty value starts with no bits set.
type Bitmap struct {
	rb *roaring.Bitmap
}

func (me *Bitmap) Len() int {
	if me.rb == nil {
		return 0
	}
	return int(me.rb.GetCardinality())
}

func (me Bitmap) ToSortedSlice() (ret []int) {
	if me.rb == nil {
		return
	}
	missinggo.CastSlice(&ret, me.rb.ToArray())
	return
}

func (me *Bitmap) lazyRB() *roaring.Bitmap {
	if me.rb == nil {
		me.rb = roaring.NewBitmap()
	}
	return me.rb
}

func (me *Bitmap) Iter(f func(interface{}) bool) {
	me.IterTyped(func(i int) bool {
		return f(i)
	})
}

func (me Bitmap) IterTyped(f func(int) bool) bool {
	if me.rb == nil {
		return true
	}
	it := me.rb.Iterator()
	for it.HasNext() {
		if !f(int(it.Next())) {
			return false
		}
	}
	return true
}

func (me *Bitmap) Add(is ...int) {
	rb := me.lazyRB()
	for _, i := range is {
		rb.AddInt(i)
	}
}

func (me *Bitmap) AddRange(begin, end int) {
	if begin >= end {
		return
	}
	me.lazyRB().AddRange(uint32(begin), uint32(end))
}

func (me *Bitmap) Remove(i int) {
	if me.rb == nil {
		return
	}
	me.rb.Remove(uint32(i))
}

func (me *Bitmap) Union(other *Bitmap) {
	me.lazyRB().Or(other.lazyRB())
}

func (me *Bitmap) Contains(i int) bool {
	if me.rb == nil {
		return false
	}
	return me.rb.Contains(uint32(i))
}

type Iter struct {
	ii roaring.IntIterable
}

func (me *Iter) Next() bool {
	if me == nil {
		return false
	}
	return me.ii.HasNext()
}

func (me *Iter) Value() interface{} {
	return me.ValueInt()
}

func (me *Iter) ValueInt() int {
	return int(me.ii.Next())
}

func (me *Iter) Stop() {}

func Sub(left, right *Bitmap) *Bitmap {
	return &Bitmap{
		rb: roaring.AndNot(left.lazyRB(), right.lazyRB()),
	}
}

func (me *Bitmap) Sub(other *Bitmap) {
	if other.rb == nil {
		return
	}
	if me.rb == nil {
		return
	}
	me.rb.AndNot(other.rb)
}

func (me *Bitmap) Clear() {
	if me.rb == nil {
		return
	}
	me.rb.Clear()
}

func (me Bitmap) Copy() (ret Bitmap) {
	ret = me
	if ret.rb != nil {
		ret.rb = ret.rb.Clone()
	}
	return
}

func (me *Bitmap) FlipRange(begin, end int) {
	me.lazyRB().FlipInt(begin, end)
}

func (me *Bitmap) Get(bit int) bool {
	return me.rb != nil && me.rb.ContainsInt(bit)
}

func (me *Bitmap) Set(bit int, value bool) {
	if value {
		me.lazyRB().AddInt(bit)
	} else {
		if me.rb != nil {
			me.rb.Remove(uint32(bit))
		}
	}
}
<|endoftext|>


<commit_before>package repo_manager

import (
	"fmt"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/skia-dev/glog"

	"go.skia.org/infra/go/autoroll"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gitinfo"
	"go.skia.org/infra/go/util"
)

const (
	DEPS_ROLL_BRANCH = "roll_branch"

	GCLIENT  = "gclient"
	ROLL_DEP = "roll-dep"

	REPO_CHROMIUM = "https://chromium.googlesource.com/chromium/src.git"

	TMPL_CQ_INCLUDE_TRYBOTS = "CQ_INCLUDE_TRYBOTS=%s"
)

var (
	ISSUE_CREATED_REGEX = regexp.MustCompile(fmt.Sprintf("Issue created. URL: %s/(\\d+)", autoroll.RIETVELD_URL))

	// Use this function to instantiate a RepoManager. This is able to be
	// overridden for testing.
	NewRepoManager func(string, string, time.Duration, string) (RepoManager, error) = NewDefaultRepoManager

	DEPOT_TOOLS_AUTH_USER_REGEX = regexp.MustCompile(fmt.Sprintf("Logged in to %s as ([\\w-]+).", autoroll.RIETVELD_URL))
)

// RepoManager is used by AutoRoller for managing checkouts.
type RepoManager interface {
	ForceUpdate() error
	FullChildHash(string) (string, error)
	LastRollRev() string
	RolledPast(string) bool
	ChildHead() string
	CreateNewRoll([]string, string, bool) (int64, error)
	User() string
}

// repoManager is a struct used by AutoRoller for managing checkouts.
type repoManager struct {
	chromiumDir       string
	chromiumParentDir string
	depot_tools       string
	gclient           string
	infoMtx           sync.RWMutex
	lastRollRev       string
	repoMtx           sync.RWMutex
	rollDep           string
	childDir          string
	childHead         string
	childPath         string
	childRepo         *gitinfo.GitInfo
	user              string
}

// getEnv returns the environment used for most commands.
func getEnv(depotTools string) []string {
	return []string{
		fmt.Sprintf("PATH=%s:%s", depotTools, os.Getenv("PATH")),
		fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
	}
}

// getDepotToolsUser returns the authorized depot tools user.
func getDepotToolsUser(depotTools string) (string, error) {
	output, err := exec.RunCommand(&exec.Command{
		Env:  getEnv(depotTools),
		Name: path.Join(depotTools, "depot-tools-auth"),
		Args: []string{"info", autoroll.RIETVELD_URL},
	})
	if err != nil {
		return "", err
	}
	m := DEPOT_TOOLS_AUTH_USER_REGEX.FindStringSubmatch(output)
	if len(m) != 2 {
		return "", fmt.Errorf("Unable to parse the output of depot-tools-auth.")
	}
	return m[1], nil
}

// NewDefaultRepoManager returns a RepoManager instance which operates in the given
// working directory and updates at the given frequency.
func NewDefaultRepoManager(workdir, childPath string, frequency time.Duration, depot_tools string) (RepoManager, error) {
	gclient := GCLIENT
	rollDep := ROLL_DEP
	if depot_tools != "" {
		gclient = path.Join(depot_tools, gclient)
		rollDep = path.Join(depot_tools, rollDep)
	}

	chromiumParentDir := path.Join(workdir, "chromium")
	chromiumDir := path.Join(chromiumParentDir, "src")

	user, err := getDepotToolsUser(depot_tools)
	if err != nil {
		return nil, fmt.Errorf("Failed to determine depot tools user: %s", err)
	}

	r := &repoManager{
		chromiumDir:       chromiumDir,
		chromiumParentDir: chromiumParentDir,
		depot_tools:       depot_tools,
		gclient:           gclient,
		rollDep:           rollDep,
		childDir:          path.Join(chromiumParentDir, childPath),
		childPath:         childPath,
		childRepo:         nil, // This will be filled in on the first update.
		user:              user,
	}
	if err := r.update(); err != nil {
		return nil, err
	}
	go func() {
		for _ = range time.Tick(frequency) {
			util.LogErr(r.update())
		}
	}()
	return r, nil
}

// update syncs code in the relevant repositories.
func (r *repoManager) update() error {
	// Sync the projects.
	r.repoMtx.Lock()
	defer r.repoMtx.Unlock()

	// Create the chromium parent directory if needed.
	if _, err := os.Stat(r.chromiumParentDir); err != nil {
		if err := os.MkdirAll(r.chromiumParentDir, 0755); err != nil {
			return err
		}
	}

	if _, err := os.Stat(path.Join(r.chromiumDir, ".git")); err == nil {
		if err := r.cleanChromium(); err != nil {
			return err
		}
		// Update the repo.
		if _, err := exec.RunCwd(r.chromiumDir, "git", "fetch"); err != nil {
			return err
		}
		if _, err := exec.RunCwd(r.chromiumDir, "git", "reset", "--hard", "origin/master"); err != nil {
			return err
		}
	}

	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumParentDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"config", REPO_CHROMIUM},
	}); err != nil {
		return err
	}
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumParentDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"sync", "--nohooks"},
	}); err != nil {
		return err
	}

	// Create the child GitInfo if needed.
	if r.childRepo == nil {
		childRepo, err := gitinfo.NewGitInfo(r.childDir, false, true)
		if err != nil {
			return err
		}
		r.childRepo = childRepo
	}

	// Get the last roll revision.
	lastRollRev, err := r.getLastRollRev()
	if err != nil {
		return err
	}

	// Record child HEAD
	childHead, err := r.childRepo.FullHash("origin/master")
	if err != nil {
		return err
	}
	r.infoMtx.Lock()
	defer r.infoMtx.Unlock()
	r.lastRollRev = lastRollRev
	r.childHead = childHead
	return nil
}

// ForceUpdate forces the repoManager to update.
func (r *repoManager) ForceUpdate() error {
	return r.update()
}

// getLastRollRev returns the commit hash of the last-completed DEPS roll.
func (r *repoManager) getLastRollRev() (string, error) {
	output, err := exec.RunCwd(r.chromiumDir, r.gclient, "revinfo")
	if err != nil {
		return "", err
	}
	split := strings.Split(output, "\n")
	for _, s := range split {
		if strings.HasPrefix(s, r.childPath) {
			subs := strings.Split(s, "@")
			if len(subs) != 2 {
				return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
			}
			return subs[1], nil
		}
	}
	return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
}

// FullChildHash returns the full hash of the given short hash or ref in the
// child repo.
func (r *repoManager) FullChildHash(shortHash string) (string, error) {
	r.repoMtx.RLock()
	defer r.repoMtx.RUnlock()
	return r.childRepo.FullHash(shortHash)
}

// LastRollRev returns the last-rolled child commit.
func (r *repoManager) LastRollRev() string {
	r.infoMtx.RLock()
	defer r.infoMtx.RUnlock()
	return r.lastRollRev
}

// RolledPast determines whether DEPS has rolled past the given commit.
func (r *repoManager) RolledPast(hash string) bool {
	r.repoMtx.RLock()
	defer r.repoMtx.RUnlock()
	if _, err := exec.RunCwd(r.childDir, "git", "merge-base", "--is-ancestor", hash, r.lastRollRev); err != nil {
		return false
	}
	return true
}

// ChildHead returns the current child origin/master branch head.
func (r *repoManager) ChildHead() string {
	r.infoMtx.RLock()
	defer r.infoMtx.RUnlock()
	return r.childHead
}

// cleanChromium forces the Chromium checkout into a clean state.
func (r *repoManager) cleanChromium() error {
	if _, err := exec.RunCwd(r.chromiumDir, "git", "clean", "-d", "-f", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "rebase", "--abort")
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "origin/master", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "branch", "-D", DEPS_ROLL_BRANCH)
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"revert", "--nohooks"},
	}); err != nil {
		return err
	}
	return nil
}

// CreateNewRoll creates and uploads a new DEPS roll to the given commit.
// Returns the issue number of the uploaded roll.
func (r *repoManager) CreateNewRoll(emails []string, cqExtraTrybots string, dryRun bool) (int64, error) {
	r.repoMtx.Lock()
	defer r.repoMtx.Unlock()

	// Clean the checkout, get onto a fresh branch.
	if err := r.cleanChromium(); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "-b", DEPS_ROLL_BRANCH, "-t", "origin/master", "-f"); err != nil {
		return 0, err
	}

	// Defer some more cleanup.
	defer func() {
		util.LogErr(r.cleanChromium())
	}()

	// Create the roll CL.
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.name", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.email", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}

	// Find Chromium bugs.
	bugs := []string{}
	cr := r.childRepo
	commits, err := cr.RevList(fmt.Sprintf("%s..%s", r.lastRollRev, r.childHead))
	if err != nil {
		return 0, fmt.Errorf("Failed to list revisions: %s", err)
	}
	for _, c := range commits {
		d, err := cr.Details(c, false)
		if err != nil {
			return 0, fmt.Errorf("Failed to obtain commit details: %s", err)
		}
		b := util.BugsFromCommitMsg(d.Body)
		for _, bug := range b[util.PROJECT_CHROMIUM] {
			bugs = append(bugs, bug)
		}
	}

	args := []string{r.childPath, r.childHead}
	if len(bugs) > 0 {
		args = append(args, "--bug", strings.Join(bugs, ","))
	}
	glog.Infof("Running command: roll-dep %s", strings.Join(args, " "))
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: r.rollDep,
		Args: args,
	}); err != nil {
		return 0, err
	}
	// Build the commit message, starting with the message provided by roll-dep.
	commitMsg, err := exec.RunCwd(r.chromiumDir, "git", "log", "-n1", "--format=%B", "HEAD")
	if err != nil {
		return 0, err
	}
	if cqExtraTrybots != "" {
		commitMsg += "\n" + fmt.Sprintf(TMPL_CQ_INCLUDE_TRYBOTS, cqExtraTrybots)
	}
	uploadCmd := &exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: "git",
		Args: []string{"cl", "upload", "--bypass-hooks", "-f"},
	}
	if dryRun {
		uploadCmd.Args = append(uploadCmd.Args, "--cq-dry-run")
	} else {
		uploadCmd.Args = append(uploadCmd.Args, "--use-commit-queue")
	}
	tbr := "\nTBR="
	if emails != nil && len(emails) > 0 {
		emailStr := strings.Join(emails, ",")
		tbr += emailStr
		uploadCmd.Args = append(uploadCmd.Args, "--send-mail", "--cc", emailStr)
	}
	commitMsg += tbr
	uploadCmd.Args = append(uploadCmd.Args, "-m", commitMsg)

	// Upload the CL.
	uploadOutput, err := exec.RunCommand(uploadCmd)
	if err != nil {
		return 0, err
	}
	issues := ISSUE_CREATED_REGEX.FindStringSubmatch(uploadOutput)
	if len(issues) != 2 {
		return 0, fmt.Errorf("Failed to find newly-uploaded issue number!")
	}
	return strconv.ParseInt(issues[1], 10, 64)
}

func (r *repoManager) User() string {
	return r.user
}
<commit_msg>Fix autoroll issue number parsing<commit_after>package repo_manager

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/skia-dev/glog"

	"go.skia.org/infra/go/autoroll"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gitinfo"
	"go.skia.org/infra/go/util"
)

const (
	DEPS_ROLL_BRANCH = "roll_branch"

	GCLIENT  = "gclient"
	ROLL_DEP = "roll-dep"

	REPO_CHROMIUM = "https://chromium.googlesource.com/chromium/src.git"

	TMPL_CQ_INCLUDE_TRYBOTS = "CQ_INCLUDE_TRYBOTS=%s"
)

var (
	// Use this function to instantiate a RepoManager. This is able to be
	// overridden for testing.
	NewRepoManager func(string, string, time.Duration, string) (RepoManager, error) = NewDefaultRepoManager

	DEPOT_TOOLS_AUTH_USER_REGEX = regexp.MustCompile(fmt.Sprintf("Logged in to %s as ([\\w-]+).", autoroll.RIETVELD_URL))
)

// issueJson is the structure of "git cl issue --json"
type issueJson struct {
	Issue    int64  `json:"issue"`
	IssueUrl string `json:"issue_url"`
}

// RepoManager is used by AutoRoller for managing checkouts.
type RepoManager interface {
	ForceUpdate() error
	FullChildHash(string) (string, error)
	LastRollRev() string
	RolledPast(string) bool
	ChildHead() string
	CreateNewRoll([]string, string, bool) (int64, error)
	User() string
}

// repoManager is a struct used by AutoRoller for managing checkouts.
type repoManager struct {
	chromiumDir       string
	chromiumParentDir string
	depot_tools       string
	gclient           string
	infoMtx           sync.RWMutex
	lastRollRev       string
	repoMtx           sync.RWMutex
	rollDep           string
	childDir          string
	childHead         string
	childPath         string
	childRepo         *gitinfo.GitInfo
	user              string
}

// getEnv returns the environment used for most commands.
func getEnv(depotTools string) []string {
	return []string{
		fmt.Sprintf("PATH=%s:%s", depotTools, os.Getenv("PATH")),
		fmt.Sprintf("HOME=%s", os.Getenv("HOME")),
	}
}

// getDepotToolsUser returns the authorized depot tools user.
func getDepotToolsUser(depotTools string) (string, error) {
	output, err := exec.RunCommand(&exec.Command{
		Env:  getEnv(depotTools),
		Name: path.Join(depotTools, "depot-tools-auth"),
		Args: []string{"info", autoroll.RIETVELD_URL},
	})
	if err != nil {
		return "", err
	}
	m := DEPOT_TOOLS_AUTH_USER_REGEX.FindStringSubmatch(output)
	if len(m) != 2 {
		return "", fmt.Errorf("Unable to parse the output of depot-tools-auth.")
	}
	return m[1], nil
}

// NewDefaultRepoManager returns a RepoManager instance which operates in the given
// working directory and updates at the given frequency.
func NewDefaultRepoManager(workdir, childPath string, frequency time.Duration, depot_tools string) (RepoManager, error) {
	gclient := GCLIENT
	rollDep := ROLL_DEP
	if depot_tools != "" {
		gclient = path.Join(depot_tools, gclient)
		rollDep = path.Join(depot_tools, rollDep)
	}

	chromiumParentDir := path.Join(workdir, "chromium")
	chromiumDir := path.Join(chromiumParentDir, "src")

	user, err := getDepotToolsUser(depot_tools)
	if err != nil {
		return nil, fmt.Errorf("Failed to determine depot tools user: %s", err)
	}

	r := &repoManager{
		chromiumDir:       chromiumDir,
		chromiumParentDir: chromiumParentDir,
		depot_tools:       depot_tools,
		gclient:           gclient,
		rollDep:           rollDep,
		childDir:          path.Join(chromiumParentDir, childPath),
		childPath:         childPath,
		childRepo:         nil, // This will be filled in on the first update.
		user:              user,
	}
	if err := r.update(); err != nil {
		return nil, err
	}
	go func() {
		for _ = range time.Tick(frequency) {
			util.LogErr(r.update())
		}
	}()
	return r, nil
}

// update syncs code in the relevant repositories.
func (r *repoManager) update() error {
	// Sync the projects.
	r.repoMtx.Lock()
	defer r.repoMtx.Unlock()

	// Create the chromium parent directory if needed.
	if _, err := os.Stat(r.chromiumParentDir); err != nil {
		if err := os.MkdirAll(r.chromiumParentDir, 0755); err != nil {
			return err
		}
	}

	if _, err := os.Stat(path.Join(r.chromiumDir, ".git")); err == nil {
		if err := r.cleanChromium(); err != nil {
			return err
		}
		// Update the repo.
		if _, err := exec.RunCwd(r.chromiumDir, "git", "fetch"); err != nil {
			return err
		}
		if _, err := exec.RunCwd(r.chromiumDir, "git", "reset", "--hard", "origin/master"); err != nil {
			return err
		}
	}

	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumParentDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"config", REPO_CHROMIUM},
	}); err != nil {
		return err
	}
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumParentDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"sync", "--nohooks"},
	}); err != nil {
		return err
	}

	// Create the child GitInfo if needed.
	if r.childRepo == nil {
		childRepo, err := gitinfo.NewGitInfo(r.childDir, false, true)
		if err != nil {
			return err
		}
		r.childRepo = childRepo
	}

	// Get the last roll revision.
	lastRollRev, err := r.getLastRollRev()
	if err != nil {
		return err
	}

	// Record child HEAD
	childHead, err := r.childRepo.FullHash("origin/master")
	if err != nil {
		return err
	}
	r.infoMtx.Lock()
	defer r.infoMtx.Unlock()
	r.lastRollRev = lastRollRev
	r.childHead = childHead
	return nil
}

// ForceUpdate forces the repoManager to update.
func (r *repoManager) ForceUpdate() error {
	return r.update()
}

// getLastRollRev returns the commit hash of the last-completed DEPS roll.
func (r *repoManager) getLastRollRev() (string, error) {
	output, err := exec.RunCwd(r.chromiumDir, r.gclient, "revinfo")
	if err != nil {
		return "", err
	}
	split := strings.Split(output, "\n")
	for _, s := range split {
		if strings.HasPrefix(s, r.childPath) {
			subs := strings.Split(s, "@")
			if len(subs) != 2 {
				return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
			}
			return subs[1], nil
		}
	}
	return "", fmt.Errorf("Failed to parse output of `gclient revinfo`")
}

// FullChildHash returns the full hash of the given short hash or ref in the
// child repo.
func (r *repoManager) FullChildHash(shortHash string) (string, error) {
	r.repoMtx.RLock()
	defer r.repoMtx.RUnlock()
	return r.childRepo.FullHash(shortHash)
}

// LastRollRev returns the last-rolled child commit.
func (r *repoManager) LastRollRev() string {
	r.infoMtx.RLock()
	defer r.infoMtx.RUnlock()
	return r.lastRollRev
}

// RolledPast determines whether DEPS has rolled past the given commit.
func (r *repoManager) RolledPast(hash string) bool {
	r.repoMtx.RLock()
	defer r.repoMtx.RUnlock()
	if _, err := exec.RunCwd(r.childDir, "git", "merge-base", "--is-ancestor", hash, r.lastRollRev); err != nil {
		return false
	}
	return true
}

// ChildHead returns the current child origin/master branch head.
func (r *repoManager) ChildHead() string {
	r.infoMtx.RLock()
	defer r.infoMtx.RUnlock()
	return r.childHead
}

// cleanChromium forces the Chromium checkout into a clean state.
func (r *repoManager) cleanChromium() error {
	if _, err := exec.RunCwd(r.chromiumDir, "git", "clean", "-d", "-f", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "rebase", "--abort")
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "origin/master", "-f"); err != nil {
		return err
	}
	_, _ = exec.RunCwd(r.chromiumDir, "git", "branch", "-D", DEPS_ROLL_BRANCH)
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: r.gclient,
		Args: []string{"revert", "--nohooks"},
	}); err != nil {
		return err
	}
	return nil
}

// CreateNewRoll creates and uploads a new DEPS roll to the given commit.
// Returns the issue number of the uploaded roll.
func (r *repoManager) CreateNewRoll(emails []string, cqExtraTrybots string, dryRun bool) (int64, error) {
	r.repoMtx.Lock()
	defer r.repoMtx.Unlock()

	// Clean the checkout, get onto a fresh branch.
	if err := r.cleanChromium(); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "checkout", "-b", DEPS_ROLL_BRANCH, "-t", "origin/master", "-f"); err != nil {
		return 0, err
	}

	// Defer some more cleanup.
	defer func() {
		util.LogErr(r.cleanChromium())
	}()

	// Create the roll CL.
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.name", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}
	if _, err := exec.RunCwd(r.chromiumDir, "git", "config", "user.email", autoroll.ROLL_AUTHOR); err != nil {
		return 0, err
	}

	// Find Chromium bugs.
	bugs := []string{}
	cr := r.childRepo
	commits, err := cr.RevList(fmt.Sprintf("%s..%s", r.lastRollRev, r.childHead))
	if err != nil {
		return 0, fmt.Errorf("Failed to list revisions: %s", err)
	}
	for _, c := range commits {
		d, err := cr.Details(c, false)
		if err != nil {
			return 0, fmt.Errorf("Failed to obtain commit details: %s", err)
		}
		b := util.BugsFromCommitMsg(d.Body)
		for _, bug := range b[util.PROJECT_CHROMIUM] {
			bugs = append(bugs, bug)
		}
	}

	args := []string{r.childPath, r.childHead}
	if len(bugs) > 0 {
		args = append(args, "--bug", strings.Join(bugs, ","))
	}
	glog.Infof("Running command: roll-dep %s", strings.Join(args, " "))
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: r.rollDep,
		Args: args,
	}); err != nil {
		return 0, err
	}
	// Build the commit message, starting with the message provided by roll-dep.
	commitMsg, err := exec.RunCwd(r.chromiumDir, "git", "log", "-n1", "--format=%B", "HEAD")
	if err != nil {
		return 0, err
	}
	if cqExtraTrybots != "" {
		commitMsg += "\n" + fmt.Sprintf(TMPL_CQ_INCLUDE_TRYBOTS, cqExtraTrybots)
	}
	uploadCmd := &exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: "git",
		Args: []string{"cl", "upload", "--bypass-hooks", "-f"},
	}
	if dryRun {
		uploadCmd.Args = append(uploadCmd.Args, "--cq-dry-run")
	} else {
		uploadCmd.Args = append(uploadCmd.Args, "--use-commit-queue")
	}
	tbr := "\nTBR="
	if emails != nil && len(emails) > 0 {
		emailStr := strings.Join(emails, ",")
		tbr += emailStr
		uploadCmd.Args = append(uploadCmd.Args, "--send-mail", "--cc", emailStr)
	}
	commitMsg += tbr
	uploadCmd.Args = append(uploadCmd.Args, "-m", commitMsg)

	// Upload the CL.
	if _, err := exec.RunCommand(uploadCmd); err != nil {
		return 0, err
	}

	// Obtain the issue number.
	tmp, err := ioutil.TempDir("", "")
	if err != nil {
		return 0, err
	}
	defer util.RemoveAll(tmp)
	jsonFile := path.Join(tmp, "issue.json")
	if _, err := exec.RunCommand(&exec.Command{
		Dir:  r.chromiumDir,
		Env:  getEnv(r.depot_tools),
		Name: "git",
		Args: []string{"cl", "issue", fmt.Sprintf("--json=%s", jsonFile)},
	}); err != nil {
		return 0, err
	}
	f, err := os.Open(jsonFile)
	if err != nil {
		return 0, err
	}
	var issue issueJson
	if err := json.NewDecoder(f).Decode(&issue); err != nil {
		return 0, err
	}
	return issue.Issue, nil
}

func (r *repoManager) User() string {
	return r.user
}
<|endoftext|>