<commit_before>package contrib

import (
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"io"
	"time"

	"github.com/gin-gonic/gin"
)

var pid = uint32(time.Now().UnixNano() % 4294967291)

func GenReqId() string {
	var b [12]byte
	binary.LittleEndian.PutUint32(b[:], pid)
	binary.LittleEndian.PutUint64(b[4:], uint64(time.Now().UnixNano()))
	return base64.URLEncoding.EncodeToString(b[:])
}

var (
	green   = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
	white   = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
	yellow  = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
	red     = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
	blue    = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
	magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
	cyan    = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
	reset   = string([]byte{27, 91, 48, 109})
)

func ErrorLogger() gin.HandlerFunc {
	return ErrorLoggerT(gin.ErrorTypeAny)
}

func ErrorLoggerT(typ gin.ErrorType) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		// avoid writting if we already wrote into the response body
		if !c.Writer.Written() {
			errors := c.Errors.ByType(typ)
			if len(errors) > 0 {
				c.JSON(-1, errors)
			}
		}
	}
}

// Instances a Logger middleware that will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
func Logger() gin.HandlerFunc {
	return LoggerWithWriter(gin.DefaultWriter)
}

// Instance a Logger middleware with the specified writter buffer.
// Example: os.Stdout, a file opened in write mode, a socket...
func LoggerWithWriter(out io.Writer, notlogged ...string) gin.HandlerFunc {
	var skip map[string]struct{}

	if length := len(notlogged); length > 0 {
		skip = make(map[string]struct{}, length)

		for _, path := range notlogged {
			skip[path] = struct{}{}
		}
	}

	return func(c *gin.Context) {
		// Start timer
		start := time.Now()

		xReqid := c.Request.Header.Get("X-Reqid")

		if xReqid == "" {
			xReqid = GenReqId()
		}

		c.Header("X-Reqid", xReqid)

		path := c.Request.URL.RequestURI()

		clientIP := c.ClientIP()
		method := c.Request.Method
		methodColor := colorForMethod(method)

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			fmt.Fprintf(out, "[GIN] [%s] [Route Start]\t%v |%s  %s %-7s %s\n",
				xReqid,
				start.Format("2006/01/02 - 15:04:05"),
				methodColor, reset, method,
				path,
			)
			fmt.Printf("%+v", c.Request)
		}

		// Process request
		c.Next()

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			// Stop timer
			end := time.Now()
			latency := end.Sub(start)

			comment := c.Errors.ByType(gin.ErrorTypePrivate).String()

			statusCode := c.Writer.Status()
			statusColor := colorForStatus(statusCode)
			methodColor = colorForMethod(method)

			fmt.Fprintf(out, "[GIN] [%s] [Route End]\t%v |%s %3d %s| %13v | %s |%s  %s %-7s %s\n%s",
				xReqid,
				end.Format("2006/01/02 - 15:04:05"),
				statusColor, statusCode, reset,
				latency,
				clientIP,
				methodColor, reset, method,
				path,
				comment,
			)
		}
	}
}

func colorForStatus(code int) string {
	switch {
	case code >= 200 && code < 300:
		return green
	case code >= 300 && code < 400:
		return white
	case code >= 400 && code < 500:
		return yellow
	default:
		return red
	}
}

func colorForMethod(method string) string {
	switch method {
	case "GET":
		return blue
	case "POST":
		return cyan
	case "PUT":
		return yellow
	case "DELETE":
		return red
	case "PATCH":
		return green
	case "HEAD":
		return magenta
	case "OPTIONS":
		return white
	default:
		return reset
	}
}
<commit_msg>Update context request log print<commit_after>package contrib

import (
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"io"
	"time"

	"github.com/gin-gonic/gin"
)

var pid = uint32(time.Now().UnixNano() % 4294967291)

func GenReqId() string {
	var b [12]byte
	binary.LittleEndian.PutUint32(b[:], pid)
	binary.LittleEndian.PutUint64(b[4:], uint64(time.Now().UnixNano()))
	return base64.URLEncoding.EncodeToString(b[:])
}

var (
	green   = string([]byte{27, 91, 57, 55, 59, 52, 50, 109})
	white   = string([]byte{27, 91, 57, 48, 59, 52, 55, 109})
	yellow  = string([]byte{27, 91, 57, 55, 59, 52, 51, 109})
	red     = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
	blue    = string([]byte{27, 91, 57, 55, 59, 52, 52, 109})
	magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109})
	cyan    = string([]byte{27, 91, 57, 55, 59, 52, 54, 109})
	reset   = string([]byte{27, 91, 48, 109})
)

func ErrorLogger() gin.HandlerFunc {
	return ErrorLoggerT(gin.ErrorTypeAny)
}

func ErrorLoggerT(typ gin.ErrorType) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		// avoid writting if we already wrote into the response body
		if !c.Writer.Written() {
			errors := c.Errors.ByType(typ)
			if len(errors) > 0 {
				c.JSON(-1, errors)
			}
		}
	}
}

// Instances a Logger middleware that will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
func Logger() gin.HandlerFunc {
	return LoggerWithWriter(gin.DefaultWriter)
}

// Instance a Logger middleware with the specified writter buffer.
// Example: os.Stdout, a file opened in write mode, a socket...
func LoggerWithWriter(out io.Writer, notlogged ...string) gin.HandlerFunc {
	var skip map[string]struct{}

	if length := len(notlogged); length > 0 {
		skip = make(map[string]struct{}, length)

		for _, path := range notlogged {
			skip[path] = struct{}{}
		}
	}

	return func(c *gin.Context) {
		// Start timer
		start := time.Now()

		xReqid := c.Request.Header.Get("X-Reqid")

		if xReqid == "" {
			xReqid = GenReqId()
		}

		c.Header("X-Reqid", xReqid)

		path := c.Request.URL.RequestURI()

		clientIP := c.ClientIP()
		method := c.Request.Method
		methodColor := colorForMethod(method)

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			fmt.Fprintf(out, "[GIN] [%s] [Route Start]\t%v |%s  %s %-7s %s\n",
				xReqid,
				start.Format("2006/01/02 - 15:04:05"),
				methodColor, reset, method,
				path,
			)
			fmt.Printf("%+v\n", c.Request)
		}

		// Process request
		c.Next()

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			// Stop timer
			end := time.Now()
			latency := end.Sub(start)

			comment := c.Errors.ByType(gin.ErrorTypePrivate).String()

			statusCode := c.Writer.Status()
			statusColor := colorForStatus(statusCode)
			methodColor = colorForMethod(method)

			fmt.Fprintf(out, "[GIN] [%s] [Route End]\t%v |%s %3d %s| %13v | %s |%s  %s %-7s %s\n%s",
				xReqid,
				end.Format("2006/01/02 - 15:04:05"),
				statusColor, statusCode, reset,
				latency,
				clientIP,
				methodColor, reset, method,
				path,
				comment,
			)
		}
	}
}

func colorForStatus(code int) string {
	switch {
	case code >= 200 && code < 300:
		return green
	case code >= 300 && code < 400:
		return white
	case code >= 400 && code < 500:
		return yellow
	default:
		return red
	}
}

func colorForMethod(method string) string {
	switch method {
	case "GET":
		return blue
	case "POST":
		return cyan
	case "PUT":
		return yellow
	case "DELETE":
		return red
	case "PATCH":
		return green
	case "HEAD":
		return magenta
	case "OPTIONS":
		return white
	default:
		return reset
	}
}
<|endoftext|>


<commit_before>package logger

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

const (
	_VER string = "1.0.0"
)

type LEVEL int32

var logLevel LEVEL = 1
var maxFileSize int64
var maxFileCount int32
var dailyRolling bool = true
var consoleAppender bool = true
var RollingFile bool = false
var logObj *_FILE

const DATEFORMAT = "2006-01-02"

type UNIT int64

const (
	_       = iota
	KB UNIT = 1 << (iota * 10)
	MB
	GB
	TB
)

const (
	ALL LEVEL = iota
	DEBUG
	INFO
	WARN
	ERROR
	FATAL
	OFF
)

type _FILE struct {
	dir      string
	filename string
	_suffix  int
	isCover  bool
	_date    *time.Time
	mu       *sync.RWMutex
	logfile  *os.File
	lg       *log.Logger
}

//指定是否控制台打印，默认为true
func SetConsole(isConsole bool) {
	consoleAppender = isConsole
}

//指定日志级别  ALL，DEBUG，INFO，WARN，ERROR，FATAL，OFF 级别由低到高
//一般习惯是测试阶段为debug，生成环境为info以上
func SetLevel(_level LEVEL) {
	logLevel = _level
}

//默认方式新建日志，日志不进行分割
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
func Open(fileDir, fileName string) {
	RollingFile = false
	dailyRolling = false
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()

	logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
	logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
}

//指定日志文件备份方式为日期的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
func OpenRollDaily(fileDir, fileName string) {
	RollingFile = false
	dailyRolling = true
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()

	if !logObj.isMustRename() {
		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
	} else {
		logObj.rename()
	}
}

//指定日志文件备份方式为文件大小的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
//第三个参数为备份文件最大数量
//第四个参数为备份文件大小
//第五个参数为文件大小的单位
//logger.OpenRollSize("/var/log", "test.log", 10, 100, logger.MB)
func OpenRollSize(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
	maxFileCount = maxNumber
	maxFileSize = maxSize * int64(_unit)
	RollingFile = true
	dailyRolling = false
	logObj = &_FILE{dir: fileDir, filename: fileName, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()
	for i := 1; i <= int(maxNumber); i++ {
		if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
			logObj._suffix = i
		} else {
			break
		}
	}
	if !logObj.isMustRename() {
		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
	} else {
		logObj.rename()
	}
	go fileMonitor()
}

func Close() {
	logObj.mu.RLock()
	if logObj.logfile != nil {
		logObj.logfile.Close()
	}
}

func console(format string, s ...interface{}) {
	if consoleAppender {
		_, file, line, _ := runtime.Caller(2)
		short := file
		for i := len(file) - 1; i > 0; i-- {
			if file[i] == '/' {
				short = file[i+1:]
				break
			}
		}
		file = short
		//log.Println(file+":"+strconv.Itoa(line), s)
		log.Printf(file+":"+strconv.Itoa(line)+": "+format, s...)
	}
}
func consoleln(s ...interface{}) {
	if consoleAppender {
		_, file, line, _ := runtime.Caller(2)
		short := file
		for i := len(file) - 1; i > 0; i-- {
			if file[i] == '/' {
				short = file[i+1:]
				break
			}
		}
		file = short
		log.Println(file+":"+strconv.Itoa(line), s)
	}
}

func catchError() {
	if err := recover(); err != nil {
		log.Println("err", err)
	}
}

func Debugln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()

	if logLevel <= DEBUG {
		logObj.lg.Output(2, fmt.Sprintln("[DEBUG]", v))
		consoleln("[DEBUG]", v)
	}
}
func Infoln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= INFO {
		logObj.lg.Output(2, fmt.Sprintln("[INFO]", v))
		consoleln("[INFO]", v)
	}
}
func Warnln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= WARN {
		logObj.lg.Output(2, fmt.Sprintln("[WARNING]", v))
		consoleln("[WARNING]", v)
	}
}
func Errorln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= ERROR {
		logObj.lg.Output(2, fmt.Sprintln("[ERROR]", v))
		consoleln("[ERROR]", v)
	}
}
func Fatalln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= FATAL {
		logObj.lg.Output(2, fmt.Sprintln("[FATAL]", v))
		consoleln("[FATAL]", v)
	}
}

func Debug(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()

	if logLevel <= DEBUG {
		logObj.lg.Output(2, fmt.Sprintf("[DEBUG] "+format, v...))
		console("[DEBUG] "+format, v...)
	}
}
func Info(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= INFO {
		logObj.lg.Output(2, fmt.Sprintf("[INFO] "+format, v...))
		console("[INFO] "+format, v...)
	}
}
func Warn(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= WARN {
		logObj.lg.Output(2, fmt.Sprintf("[WARNING] "+format, v...))
		console("[WARNING] "+format, v...)
	}
}
func Error(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= ERROR {
		logObj.lg.Output(2, fmt.Sprintf("[ERROR] "+format, v...))
		console("[ERROR] "+format, v...)
	}
}
func Fatal(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= FATAL {
		logObj.lg.Output(2, fmt.Sprintf("[FATAL] "+format, v...))
		console("[FATAL] "+format, v...)
	}
}

func (f *_FILE) isMustRename() bool {
	if dailyRolling {
		t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
		if t.After(*f._date) {
			return true
		}
	} else {
		if maxFileCount > 1 {
			if fileSize(f.dir+"/"+f.filename) >= maxFileSize {
				return true
			}
		}
	}
	return false
}

func (f *_FILE) rename() {
	if dailyRolling {
		fn := f.dir + "/" + f.filename + "." + f._date.Format(DATEFORMAT)
		if !isExist(fn) && f.isMustRename() {
			if f.logfile != nil {
				f.logfile.Close()
			}
			err := os.Rename(f.dir+"/"+f.filename, fn)
			if err != nil {
				f.lg.Println("rename err", err.Error())
			}
			t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
			f._date = &t
			f.logfile, _ = os.Create(f.dir + "/" + f.filename)
			f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
		}
	} else {
		f.coverNextOne()
	}
}

func (f *_FILE) nextSuffix() int {
	return int(f._suffix%int(maxFileCount) + 1)
}

func (f *_FILE) coverNextOne() {
	f._suffix = f.nextSuffix()
	if f.logfile != nil {
		f.logfile.Close()
	}
	if isExist(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix))) {
		os.Remove(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix)))
	}
	os.Rename(f.dir+"/"+f.filename, f.dir+"/"+f.filename+"."+strconv.Itoa(int(f._suffix)))
	f.logfile, _ = os.Create(f.dir + "/" + f.filename)
	f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
}

func fileSize(file string) int64 {
	fmt.Println("fileSize", file)
	f, e := os.Stat(file)
	if e != nil {
		fmt.Println(e.Error())
		return 0
	}
	return f.Size()
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func fileMonitor() {
	timer := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-timer.C:
			fileCheck()
		}
	}
}

func fileCheck() {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	if logObj != nil && logObj.isMustRename() {
		logObj.mu.Lock()
		defer logObj.mu.Unlock()
		logObj.rename()
	}
}
<commit_msg>buaazp: rename logger.WARNING<commit_after>package logger

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
)

const (
	_VER string = "1.0.0"
)

type LEVEL int32

var logLevel LEVEL = 1
var maxFileSize int64
var maxFileCount int32
var dailyRolling bool = true
var consoleAppender bool = true
var RollingFile bool = false
var logObj *_FILE

const DATEFORMAT = "2006-01-02"

type UNIT int64

const (
	_       = iota
	KB UNIT = 1 << (iota * 10)
	MB
	GB
	TB
)

const (
	ALL LEVEL = iota
	DEBUG
	INFO
	WARNING
	ERROR
	FATAL
	OFF
)

type _FILE struct {
	dir      string
	filename string
	_suffix  int
	isCover  bool
	_date    *time.Time
	mu       *sync.RWMutex
	logfile  *os.File
	lg       *log.Logger
}

//指定是否控制台打印，默认为true
func SetConsole(isConsole bool) {
	consoleAppender = isConsole
}

//指定日志级别  ALL，DEBUG，INFO，WARN，ERROR，FATAL，OFF 级别由低到高
//一般习惯是测试阶段为debug，生成环境为info以上
func SetLevel(_level LEVEL) {
	logLevel = _level
}

//默认方式新建日志，日志不进行分割
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
func Open(fileDir, fileName string) {
	RollingFile = false
	dailyRolling = false
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()

	logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
	logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
}

//指定日志文件备份方式为日期的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
func OpenRollDaily(fileDir, fileName string) {
	RollingFile = false
	dailyRolling = true
	t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
	logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()

	if !logObj.isMustRename() {
		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
	} else {
		logObj.rename()
	}
}

//指定日志文件备份方式为文件大小的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
//第三个参数为备份文件最大数量
//第四个参数为备份文件大小
//第五个参数为文件大小的单位
//logger.OpenRollSize("/var/log", "test.log", 10, 100, logger.MB)
func OpenRollSize(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
	maxFileCount = maxNumber
	maxFileSize = maxSize * int64(_unit)
	RollingFile = true
	dailyRolling = false
	logObj = &_FILE{dir: fileDir, filename: fileName, isCover: false, mu: new(sync.RWMutex)}
	logObj.mu.Lock()
	defer logObj.mu.Unlock()
	for i := 1; i <= int(maxNumber); i++ {
		if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
			logObj._suffix = i
		} else {
			break
		}
	}
	if !logObj.isMustRename() {
		logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		logObj.lg = log.New(logObj.logfile, "", log.Ldate|log.Ltime|log.Lshortfile)
	} else {
		logObj.rename()
	}
	go fileMonitor()
}

func Close() {
	logObj.mu.RLock()
	if logObj.logfile != nil {
		logObj.logfile.Close()
	}
}

func console(format string, s ...interface{}) {
	if consoleAppender {
		_, file, line, _ := runtime.Caller(2)
		short := file
		for i := len(file) - 1; i > 0; i-- {
			if file[i] == '/' {
				short = file[i+1:]
				break
			}
		}
		file = short
		//log.Println(file+":"+strconv.Itoa(line), s)
		log.Printf(file+":"+strconv.Itoa(line)+": "+format, s...)
	}
}
func consoleln(s ...interface{}) {
	if consoleAppender {
		_, file, line, _ := runtime.Caller(2)
		short := file
		for i := len(file) - 1; i > 0; i-- {
			if file[i] == '/' {
				short = file[i+1:]
				break
			}
		}
		file = short
		log.Println(file+":"+strconv.Itoa(line), s)
	}
}

func catchError() {
	if err := recover(); err != nil {
		log.Println("err", err)
	}
}

func Debugln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()

	if logLevel <= DEBUG {
		logObj.lg.Output(2, fmt.Sprintln("[DEBUG]", v))
		consoleln("[DEBUG]", v)
	}
}
func Infoln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= INFO {
		logObj.lg.Output(2, fmt.Sprintln("[INFO]", v))
		consoleln("[INFO]", v)
	}
}
func Warnln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= WARNING {
		logObj.lg.Output(2, fmt.Sprintln("[WARNING]", v))
		consoleln("[WARNING]", v)
	}
}
func Errorln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= ERROR {
		logObj.lg.Output(2, fmt.Sprintln("[ERROR]", v))
		consoleln("[ERROR]", v)
	}
}
func Fatalln(v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= FATAL {
		logObj.lg.Output(2, fmt.Sprintln("[FATAL]", v))
		consoleln("[FATAL]", v)
	}
}

func Debug(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()

	if logLevel <= DEBUG {
		logObj.lg.Output(2, fmt.Sprintf("[DEBUG] "+format, v...))
		console("[DEBUG] "+format, v...)
	}
}
func Info(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= INFO {
		logObj.lg.Output(2, fmt.Sprintf("[INFO] "+format, v...))
		console("[INFO] "+format, v...)
	}
}
func Warn(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= WARNING {
		logObj.lg.Output(2, fmt.Sprintf("[WARNING] "+format, v...))
		console("[WARNING] "+format, v...)
	}
}
func Error(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= ERROR {
		logObj.lg.Output(2, fmt.Sprintf("[ERROR] "+format, v...))
		console("[ERROR] "+format, v...)
	}
}
func Fatal(format string, v ...interface{}) {
	if dailyRolling {
		fileCheck()
	}
	defer catchError()
	logObj.mu.RLock()
	defer logObj.mu.RUnlock()
	if logLevel <= FATAL {
		logObj.lg.Output(2, fmt.Sprintf("[FATAL] "+format, v...))
		console("[FATAL] "+format, v...)
	}
}

func (f *_FILE) isMustRename() bool {
	if dailyRolling {
		t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
		if t.After(*f._date) {
			return true
		}
	} else {
		if maxFileCount > 1 {
			if fileSize(f.dir+"/"+f.filename) >= maxFileSize {
				return true
			}
		}
	}
	return false
}

func (f *_FILE) rename() {
	if dailyRolling {
		fn := f.dir + "/" + f.filename + "." + f._date.Format(DATEFORMAT)
		if !isExist(fn) && f.isMustRename() {
			if f.logfile != nil {
				f.logfile.Close()
			}
			err := os.Rename(f.dir+"/"+f.filename, fn)
			if err != nil {
				f.lg.Println("rename err", err.Error())
			}
			t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
			f._date = &t
			f.logfile, _ = os.Create(f.dir + "/" + f.filename)
			f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
		}
	} else {
		f.coverNextOne()
	}
}

func (f *_FILE) nextSuffix() int {
	return int(f._suffix%int(maxFileCount) + 1)
}

func (f *_FILE) coverNextOne() {
	f._suffix = f.nextSuffix()
	if f.logfile != nil {
		f.logfile.Close()
	}
	if isExist(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix))) {
		os.Remove(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix)))
	}
	os.Rename(f.dir+"/"+f.filename, f.dir+"/"+f.filename+"."+strconv.Itoa(int(f._suffix)))
	f.logfile, _ = os.Create(f.dir + "/" + f.filename)
	f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
}

func fileSize(file string) int64 {
	fmt.Println("fileSize", file)
	f, e := os.Stat(file)
	if e != nil {
		fmt.Println(e.Error())
		return 0
	}
	return f.Size()
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func fileMonitor() {
	timer := time.NewTicker(1 * time.Second)
	for {
		select {
		case <-timer.C:
			fileCheck()
		}
	}
}

func fileCheck() {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
		}
	}()
	if logObj != nil && logObj.isMustRename() {
		logObj.mu.Lock()
		defer logObj.mu.Unlock()
		logObj.rename()
	}
}
<|endoftext|>


<commit_before>// All this package really does is add a little structure and a little color
// to the standard log package

package logger

import (
	"io"
	"log"
	"os"

	"github.com/fatih/color"
)

type Logger struct {
	out io.Writer
}

func New(out io.Writer) *Logger {
	return &Logger{out: out}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()

func (l *Logger) Info(v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(l.out, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Warn(v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(l.out, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Fatal(v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	panic(logger)
}

func (l *Logger) Infof(format string, v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(l.out, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(l.out, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	os.Exit(1)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	panic(logger)
}
<commit_msg>Fatal shouldn't print anything other than what the user prints<commit_after>// All this package really does is add a little structure and a little color
// to the standard log package

package logger

import (
	"io"
	"log"
	"os"

	"github.com/fatih/color"
)

type Logger struct {
	out io.Writer
}

func New(out io.Writer) *Logger {
	return &Logger{out: out}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()

func (l *Logger) Info(v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(l.out, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Warn(v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(l.out, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Fatal(v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(l.out, prefix+" -- ", 0)
	logger.Println(v...)
	os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	panic(logger)
}

func (l *Logger) Infof(format string, v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(l.out, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(l.out, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(l.out, prefix+" -- ", 0)
	logger.Printf(format, v...)
	os.Exit(1)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(l.out, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	panic(logger)
}
<|endoftext|>


<commit_before>package events

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// DefaultLogger is the default logger used by the Log function. This may be
// overwritten by the program to change the default route for log events.
var DefaultLogger = NewLogger(nil)

// Log emits a log event to the default logger.
func Log(format string, args ...interface{}) {
	DefaultLogger.log(1, false, format, args...)
}

// Debug emits a debug event to the default logger.
func Debug(format string, args ...interface{}) {
	DefaultLogger.debug(1, format, args...)
}

// A Logger is a wrapper around an event handler which exposes a Log method for
// formatting event messages before sending them to the handler.
//
// The format supported by the Log method is a superset of the fmt-style format,
// where the 'verbs' may include a column-surrounded value representing the name
// of the matching argument.
//
// The Log method also makes a special case when it gets an events.Args as last
// argument, it doesn't use it to format the message and instead simply append
// it to the event's argument list.
//
// Here's an example with the defalut logger:
//
//	events.Log("Hello %{name}s!", "Luke", events.Args{
//		{"from", "Han"},
//	})
//
// Which produces an event that looks like this:
//
//	events.Event{
//		Message: "Hello Luke!",
//		Args:    events.Args{{"name", "Luke"}, {"from", "Han"}},
//		...
//	}
//
// Logger instances are safe to use concurrently from multiple goroutines.
type Logger struct {
	// Handler is the event handler receiving events from the logger.
	Handler Handler

	// Args is a list of arguments that get automatically injected into every
	// events produced by the logger.
	Args Args

	// CallDepth is used to adjust which caller the logger should report its
	// events are coming from.
	// Leaving to zero means reporting the direct caller of the logger's
	// methods.
	CallDepth int

	// EnableSource controls whether the logger should report the program counter
	// address of its caller on the events it produces.
	// This has a significant impact on the performance of the logger's Log and
	// Debug method but also provides very important insights, be mindful about
	// turning it on or off.
	EnableSource bool

	// EnableDebug controls whether calls to Debug produces events.
	EnableDebug bool
}

// NewLogger allocates and returns a new logger which sends events to handler.
func NewLogger(handler Handler) *Logger {
	return &Logger{
		Handler:      handler,
		EnableSource: true,
		EnableDebug:  true,
	}
}

// Log formats an event and sends it to the logger's handler.
func (l *Logger) Log(format string, args ...interface{}) {
	l.log(1, false, format, args...)
}

func (l *Logger) log(depth int, debug bool, format string, args ...interface{}) {
	var h = l.Handler
	var s = logPool.Get().(*logState)
	var a Args

	if h == nil {
		h = DefaultHandler
	}

	if l.EnableSource {
		var pc [1]uintptr
		runtime.Callers(l.CallDepth+depth+2, pc[:])

		if pc[0] != 0 {
			file, line := SourceForPC(pc[0])
			s.src = append(s.src, file...)
			s.src = append(s.src, ':')
			s.src = strconv.AppendUint(s.src, uint64(line), 10)
		}
	}

	if n := len(args); n != 0 {
		if s, ok := args[n-1].(Args); ok {
			a, args = s, args[:n-1]
		}
	}

	s.e.Args = append(s.e.Args, l.Args...)
	s.fmt, s.e.Args = appendFormat(s.fmt, s.e.Args, format, args)
	s.e.Args = append(s.e.Args, a...)

	fmt.Fprintf(s, bytesToString(s.fmt), args...)

	s.e.Message = bytesToString(s.msg)
	s.e.Source = bytesToString(s.src)
	s.e.Debug = debug
	s.e.Time = time.Now()

	h.HandleEvent(&s.e)

	// don't hold pointers to let the garbage collector free the objects
	for i := range s.e.Args {
		s.e.Args[i] = Arg{}
	}

	s.e.Message = ""
	s.e.Source = ""
	s.e.Args = s.e.Args[:0]

	s.fmt = s.fmt[:0]
	s.msg = s.msg[:0]
	s.src = s.src[:0]

	logPool.Put(s)
	return
}

// Debug is like Log but only produces events if the logger has debugging
// enabled.
func (l *Logger) Debug(format string, args ...interface{}) {
	l.debug(1, format, args...)
}

func (l *Logger) debug(depth int, format string, args ...interface{}) {
	if l.EnableDebug {
		l.log(depth+1, true, format, args...)
	}
}

// With returns a new Logger which is a copy of l augmented with args.
func (l *Logger) With(args Args) *Logger {
	var newArgs Args
	var newLen = len(l.Args) + len(args)

	if newLen != 0 {
		newArgs = make(Args, 0, newLen)
		newArgs = append(newArgs, l.Args...)
		newArgs = append(newArgs, args...)
	}

	return &Logger{
		Args:         newArgs,
		Handler:      l.Handler,
		EnableSource: l.EnableSource,
		EnableDebug:  l.EnableDebug,
	}
}

// logState is used to build events produced by Logger instances.
type logState struct {
	e   Event
	fmt []byte
	msg []byte
	src []byte
}

func (s *logState) Write(b []byte) (n int, err error) {
	s.msg = append(s.msg, b...)
	n = len(b)
	return
}

var logPool = sync.Pool{
	New: func() interface{} {
		return &logState{
			e:   Event{Args: make(Args, 0, 8)},
			fmt: make([]byte, 0, 512),
			msg: make([]byte, 0, 512),
			src: make([]byte, 0, 512),
		}
	},
}

func appendFormat(dstFmt []byte, dstArgs Args, srcFmt string, srcArgs []interface{}) ([]byte, Args) {
	for i, j, n := 0, 0, len(srcFmt); i != n; {
		off := strings.IndexByte(srcFmt[i:], '%')
		if off < 0 {
			dstFmt = append(dstFmt, srcFmt[i:]...)
			break
		}
		off++
		dstFmt = append(dstFmt, srcFmt[i:i+off]...)

		if i += off; i != n && srcFmt[i] == '%' { // escaped '%'
			dstFmt = append(dstFmt, '%')
			i++
			continue
		}

		var key string
		var val interface{}
	fmtLoop:
		for i != n {
			switch c := srcFmt[i]; c {
			default:
				dstFmt = append(dstFmt, c)
				i++
				if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
					break fmtLoop
				}
				continue

			case '{': // extract the argument name from the format string
				if i++; i == n {
					dstFmt = append(dstFmt, '{')
					i = n
					break fmtLoop
				}

				j := strings.IndexByte(srcFmt[i:], '}')
				if j < 0 {
					dstFmt = append(dstFmt, srcFmt[i-1:]...)
					i = n
					break fmtLoop
				}

				key = srcFmt[i : i+j]
				i += j + 1
			}
		}

		if len(key) != 0 {
			if j < len(srcArgs) {
				val = srcArgs[j]
			} else {
				val = missing
			}
			dstArgs = append(dstArgs, Arg{key, val})
		}

		j++
	}

	return dstFmt, dstArgs
}

var (
	// Prevents Go from doing a memory allocation when there is a missing argument.
	missing interface{} = "MISSING"
)
<commit_msg>Fix typo in logger.go comment (#35)<commit_after>package events

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// DefaultLogger is the default logger used by the Log function. This may be
// overwritten by the program to change the default route for log events.
var DefaultLogger = NewLogger(nil)

// Log emits a log event to the default logger.
func Log(format string, args ...interface{}) {
	DefaultLogger.log(1, false, format, args...)
}

// Debug emits a debug event to the default logger.
func Debug(format string, args ...interface{}) {
	DefaultLogger.debug(1, format, args...)
}

// A Logger is a wrapper around an event handler which exposes a Log method for
// formatting event messages before sending them to the handler.
//
// The format supported by the Log method is a superset of the fmt-style format,
// where the 'verbs' may include a column-surrounded value representing the name
// of the matching argument.
//
// The Log method also makes a special case when it gets an events.Args as last
// argument, it doesn't use it to format the message and instead simply append
// it to the event's argument list.
//
// Here's an example with the default logger:
//
//	events.Log("Hello %{name}s!", "Luke", events.Args{
//		{"from", "Han"},
//	})
//
// Which produces an event that looks like this:
//
//	events.Event{
//		Message: "Hello Luke!",
//		Args:    events.Args{{"name", "Luke"}, {"from", "Han"}},
//		...
//	}
//
// Logger instances are safe to use concurrently from multiple goroutines.
type Logger struct {
	// Handler is the event handler receiving events from the logger.
	Handler Handler

	// Args is a list of arguments that get automatically injected into every
	// events produced by the logger.
	Args Args

	// CallDepth is used to adjust which caller the logger should report its
	// events are coming from.
	// Leaving to zero means reporting the direct caller of the logger's
	// methods.
	CallDepth int

	// EnableSource controls whether the logger should report the program counter
	// address of its caller on the events it produces.
	// This has a significant impact on the performance of the logger's Log and
	// Debug method but also provides very important insights, be mindful about
	// turning it on or off.
	EnableSource bool

	// EnableDebug controls whether calls to Debug produces events.
	EnableDebug bool
}

// NewLogger allocates and returns a new logger which sends events to handler.
func NewLogger(handler Handler) *Logger {
	return &Logger{
		Handler:      handler,
		EnableSource: true,
		EnableDebug:  true,
	}
}

// Log formats an event and sends it to the logger's handler.
func (l *Logger) Log(format string, args ...interface{}) {
	l.log(1, false, format, args...)
}

func (l *Logger) log(depth int, debug bool, format string, args ...interface{}) {
	var h = l.Handler
	var s = logPool.Get().(*logState)
	var a Args

	if h == nil {
		h = DefaultHandler
	}

	if l.EnableSource {
		var pc [1]uintptr
		runtime.Callers(l.CallDepth+depth+2, pc[:])

		if pc[0] != 0 {
			file, line := SourceForPC(pc[0])
			s.src = append(s.src, file...)
			s.src = append(s.src, ':')
			s.src = strconv.AppendUint(s.src, uint64(line), 10)
		}
	}

	if n := len(args); n != 0 {
		if s, ok := args[n-1].(Args); ok {
			a, args = s, args[:n-1]
		}
	}

	s.e.Args = append(s.e.Args, l.Args...)
	s.fmt, s.e.Args = appendFormat(s.fmt, s.e.Args, format, args)
	s.e.Args = append(s.e.Args, a...)

	fmt.Fprintf(s, bytesToString(s.fmt), args...)

	s.e.Message = bytesToString(s.msg)
	s.e.Source = bytesToString(s.src)
	s.e.Debug = debug
	s.e.Time = time.Now()

	h.HandleEvent(&s.e)

	// don't hold pointers to let the garbage collector free the objects
	for i := range s.e.Args {
		s.e.Args[i] = Arg{}
	}

	s.e.Message = ""
	s.e.Source = ""
	s.e.Args = s.e.Args[:0]

	s.fmt = s.fmt[:0]
	s.msg = s.msg[:0]
	s.src = s.src[:0]

	logPool.Put(s)
	return
}

// Debug is like Log but only produces events if the logger has debugging
// enabled.
func (l *Logger) Debug(format string, args ...interface{}) {
	l.debug(1, format, args...)
}

func (l *Logger) debug(depth int, format string, args ...interface{}) {
	if l.EnableDebug {
		l.log(depth+1, true, format, args...)
	}
}

// With returns a new Logger which is a copy of l augmented with args.
func (l *Logger) With(args Args) *Logger {
	var newArgs Args
	var newLen = len(l.Args) + len(args)

	if newLen != 0 {
		newArgs = make(Args, 0, newLen)
		newArgs = append(newArgs, l.Args...)
		newArgs = append(newArgs, args...)
	}

	return &Logger{
		Args:         newArgs,
		Handler:      l.Handler,
		EnableSource: l.EnableSource,
		EnableDebug:  l.EnableDebug,
	}
}

// logState is used to build events produced by Logger instances.
type logState struct {
	e   Event
	fmt []byte
	msg []byte
	src []byte
}

func (s *logState) Write(b []byte) (n int, err error) {
	s.msg = append(s.msg, b...)
	n = len(b)
	return
}

var logPool = sync.Pool{
	New: func() interface{} {
		return &logState{
			e:   Event{Args: make(Args, 0, 8)},
			fmt: make([]byte, 0, 512),
			msg: make([]byte, 0, 512),
			src: make([]byte, 0, 512),
		}
	},
}

func appendFormat(dstFmt []byte, dstArgs Args, srcFmt string, srcArgs []interface{}) ([]byte, Args) {
	for i, j, n := 0, 0, len(srcFmt); i != n; {
		off := strings.IndexByte(srcFmt[i:], '%')
		if off < 0 {
			dstFmt = append(dstFmt, srcFmt[i:]...)
			break
		}
		off++
		dstFmt = append(dstFmt, srcFmt[i:i+off]...)

		if i += off; i != n && srcFmt[i] == '%' { // escaped '%'
			dstFmt = append(dstFmt, '%')
			i++
			continue
		}

		var key string
		var val interface{}
	fmtLoop:
		for i != n {
			switch c := srcFmt[i]; c {
			default:
				dstFmt = append(dstFmt, c)
				i++
				if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
					break fmtLoop
				}
				continue

			case '{': // extract the argument name from the format string
				if i++; i == n {
					dstFmt = append(dstFmt, '{')
					i = n
					break fmtLoop
				}

				j := strings.IndexByte(srcFmt[i:], '}')
				if j < 0 {
					dstFmt = append(dstFmt, srcFmt[i-1:]...)
					i = n
					break fmtLoop
				}

				key = srcFmt[i : i+j]
				i += j + 1
			}
		}

		if len(key) != 0 {
			if j < len(srcArgs) {
				val = srcArgs[j]
			} else {
				val = missing
			}
			dstArgs = append(dstArgs, Arg{key, val})
		}

		j++
	}

	return dstFmt, dstArgs
}

var (
	// Prevents Go from doing a memory allocation when there is a missing argument.
	missing interface{} = "MISSING"
)
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"gnd.la/util/parseutil"
	"gnd.la/util/stringutil"
)

const (
	LogDir = "/var/log/governator"
)

var (
	// Altered during tests
	logDir = LogDir
)

type Out struct {
	Logger *Logger
	prefix string
	buf    []byte
}

func (o *Out) Write(b []byte) (int, error) {
	if len(o.buf) == 0 && bytes.IndexByte(b, '\n') == (len(b)-1) {
		o.Logger.Write(o.prefix, b)
	} else {
		o.buf = append(o.buf, b...)
		p := bytes.IndexByte(o.buf, '\n')
		for p >= 0 {
			rem := len(o.buf) - p - 1
			o.Logger.Write(o.prefix, o.buf[:p+1])
			if rem > 0 {
				copy(o.buf, o.buf[p+1:])
			}
			o.buf = o.buf[:rem]
			p = bytes.IndexByte(o.buf, '\n')
		}
	}
	return len(b), nil
}

type Writer interface {
	Open(string) error
	Close() error
	Write(string, []byte) error
	Flush() error
}

type LogMonitor func(string, []byte)

type Logger struct {
	Name    string
	w       Writer
	Stdout  *Out
	Stderr  *Out
	Monitor LogMonitor
	buf     []byte
	mu      sync.Mutex
}

func (l *Logger) Open() error {
	return l.w.Open(l.Name)
}

func (l *Logger) Close() error {
	return l.w.Close()
}

func (l *Logger) Write(prefix string, b []byte) error {
	if len(b) == 0 {
		return nil
	}
	now := time.Now().Unix()
	l.mu.Lock()
	defer l.mu.Unlock()
	l.buf = strconv.AppendInt(l.buf[:0], now, 10)
	l.buf = append(l.buf, ' ', '-', ' ')
	l.buf = append(l.buf, b...)
	if b[len(b)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}
	if err := l.w.Write(prefix, l.buf); err != nil {
		return err
	}
	if err := l.w.Flush(); err != nil {
		return err
	}
	if l.Monitor != nil {
		l.Monitor(prefix, l.buf)
	}
	return nil
}

func (l *Logger) WriteString(prefix string, s string) {
	l.Write(prefix, []byte(s))
}

func (l *Logger) Flush() {
	l.w.Flush()
}

func (l *Logger) Parse(input string) error {
	if input == "" {
		input = "file"
	}
	args, err := stringutil.SplitFields(input, " ")
	if err != nil {
		return err
	}
	switch strings.ToLower(args[0]) {
	case "file":
		maxSize := uint64(500 * 1024 * 1024) // 500MB
		count := 10                          // 10 rotated files
		switch len(args) {
		case 1:
			break
		case 3:
			c, err := strconv.Atoi(args[2])
			if err != nil {
				return fmt.Errorf("invalid file count %q, must be an integer", args[2])
			}
			count = c
			fallthrough
		case 2:
			size, err := parseutil.Size(args[1])
			if err != nil {
				return err
			}
			maxSize = size
		default:
			return fmt.Errorf("invalid number of arguments for file logger - must be one or two, %d given", len(args)-1)
		}
		l.w = &fileWriter{dir: logDir, maxSize: maxSize, count: count}
	case "syslog":
		var scheme string
		var addr string
		switch len(args) {
		case 1:
			break
		case 2:
			u, err := url.Parse(args[1])
			if err != nil {
				return fmt.Errorf("invalid syslog URL %q: %s", args[1], err)
			}
			if u.User != nil || u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
				return fmt.Errorf("invalid syslog URL %q: must not have user, nor path, nor query, nor fragment", args[1])
			}
			if u.Scheme == "" {
				return fmt.Errorf("invalid syslog URL %q: scheme can't be empty", args[1])
			}
			if u.Host == "" {
				return fmt.Errorf("invalid syslog URL %q: host can't be empty", args[1])
			}
			scheme = u.Scheme
			addr = u.Host
		default:
			return fmt.Errorf("invalid number of arguments for syslog logger - must be zero or one, %d given", len(args)-1)
		}
		l.w = &syslogWriter{scheme: scheme, addr: addr}
	case "none":
		l.w = &noneWriter{}
	default:
		return fmt.Errorf("invalid logger %s", args[0])
	}
	l.Stdout = &Out{Logger: l, prefix: "stdout"}
	l.Stderr = &Out{Logger: l, prefix: "stderr"}
	return nil
}
<commit_msg>Make Write() in Out faster<commit_after>package main

import (
	"bytes"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"gnd.la/util/parseutil"
	"gnd.la/util/stringutil"
)

const (
	LogDir = "/var/log/governator"
)

var (
	// Altered during tests
	logDir    = LogDir
	lineBreak = []byte{'\n'}
)

type Out struct {
	Logger *Logger
	prefix string
	buf    []byte
}

func (o *Out) Write(b []byte) (int, error) {
	if bytes.IndexByte(b, '\n') < 0 {
		o.buf = append(o.buf, b...)
		return len(b), nil
	}
	rem := b
	for len(rem) > 0 {
		p := bytes.LastIndex(rem, lineBreak)
		if p < 0 {
			break
		}
		line := rem[:p+1]
		rem = rem[p+1:]
		if len(o.buf) == 0 {
			// Nothing in the buffer, just write the line
			o.Logger.Write(o.prefix, line)
			continue
		}
		// Construct a line from the current line and the buffer. Doesn't
		// matter if we trash the buffer because we're going to write and then
		// discard it.
		o.buf = append(o.buf, line...)
		o.Logger.Write(o.prefix, o.buf)
		o.buf = o.buf[:0]
	}
	// Append remaining data to buffer
	o.buf = append(o.buf, rem...)
	return len(b), nil
}

type Writer interface {
	Open(string) error
	Close() error
	Write(string, []byte) error
	Flush() error
}

type LogMonitor func(string, []byte)

type Logger struct {
	Name    string
	w       Writer
	Stdout  *Out
	Stderr  *Out
	Monitor LogMonitor
	buf     []byte
	mu      sync.Mutex
}

func (l *Logger) Open() error {
	return l.w.Open(l.Name)
}

func (l *Logger) Close() error {
	return l.w.Close()
}

func (l *Logger) Write(prefix string, b []byte) error {
	if len(b) == 0 {
		return nil
	}
	now := time.Now().Unix()
	l.mu.Lock()
	defer l.mu.Unlock()
	l.buf = strconv.AppendInt(l.buf[:0], now, 10)
	l.buf = append(l.buf, ' ', '-', ' ')
	l.buf = append(l.buf, b...)
	if b[len(b)-1] != '\n' {
		l.buf = append(l.buf, '\n')
	}
	if err := l.w.Write(prefix, l.buf); err != nil {
		return err
	}
	if err := l.w.Flush(); err != nil {
		return err
	}
	if l.Monitor != nil {
		l.Monitor(prefix, l.buf)
	}
	return nil
}

func (l *Logger) WriteString(prefix string, s string) {
	l.Write(prefix, []byte(s))
}

func (l *Logger) Flush() {
	l.w.Flush()
}

func (l *Logger) Parse(input string) error {
	if input == "" {
		input = "file"
	}
	args, err := stringutil.SplitFields(input, " ")
	if err != nil {
		return err
	}
	switch strings.ToLower(args[0]) {
	case "file":
		maxSize := uint64(500 * 1024 * 1024) // 500MB
		count := 10                          // 10 rotated files
		switch len(args) {
		case 1:
			break
		case 3:
			c, err := strconv.Atoi(args[2])
			if err != nil {
				return fmt.Errorf("invalid file count %q, must be an integer", args[2])
			}
			count = c
			fallthrough
		case 2:
			size, err := parseutil.Size(args[1])
			if err != nil {
				return err
			}
			maxSize = size
		default:
			return fmt.Errorf("invalid number of arguments for file logger - must be one or two, %d given", len(args)-1)
		}
		l.w = &fileWriter{dir: logDir, maxSize: maxSize, count: count}
	case "syslog":
		var scheme string
		var addr string
		switch len(args) {
		case 1:
			break
		case 2:
			u, err := url.Parse(args[1])
			if err != nil {
				return fmt.Errorf("invalid syslog URL %q: %s", args[1], err)
			}
			if u.User != nil || u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
				return fmt.Errorf("invalid syslog URL %q: must not have user, nor path, nor query, nor fragment", args[1])
			}
			if u.Scheme == "" {
				return fmt.Errorf("invalid syslog URL %q: scheme can't be empty", args[1])
			}
			if u.Host == "" {
				return fmt.Errorf("invalid syslog URL %q: host can't be empty", args[1])
			}
			scheme = u.Scheme
			addr = u.Host
		default:
			return fmt.Errorf("invalid number of arguments for syslog logger - must be zero or one, %d given", len(args)-1)
		}
		l.w = &syslogWriter{scheme: scheme, addr: addr}
	case "none":
		l.w = &noneWriter{}
	default:
		return fmt.Errorf("invalid logger %s", args[0])
	}
	l.Stdout = &Out{Logger: l, prefix: "stdout"}
	l.Stderr = &Out{Logger: l, prefix: "stderr"}
	return nil
}
<|endoftext|>


<commit_before>package gorm

import (
	"database/sql/driver"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"time"
)

type logger interface {
	Print(v ...interface{})
}

type Logger struct {
	*log.Logger
}

var defaultLogger = Logger{log.New(os.Stdout, "\r\n", 0)}

// Format log
var sqlRegexp = regexp.MustCompile(`(\$\d+)|\?`)

func (logger Logger) Print(values ...interface{}) {
	if len(values) > 1 {
		level := values[0]
		currentTime := "\n\033[33m[" + NowFunc().Format("2006-01-02 15:04:05") + "]\033[0m"
		source := fmt.Sprintf("\033[35m(%v)\033[0m", values[1])
		messages := []interface{}{source, currentTime}

		if level == "sql" {
			// duration
			messages = append(messages, fmt.Sprintf(" \033[36;1m[%.2fms]\033[0m ", float64(values[2].(time.Duration).Nanoseconds()/1e4)/100.0))
			// sql
			var formatedValues []interface{}
			for _, value := range values[4].([]interface{}) {
				indirectValue := reflect.Indirect(reflect.ValueOf(value))
				if indirectValue.IsValid() {
					value = indirectValue.Interface()
					if t, ok := value.(time.Time); ok {
						formatedValues = append(formatedValues, fmt.Sprintf("'%v'", t.Format(time.RFC3339)))
					} else if b, ok := value.([]byte); ok {
						formatedValues = append(formatedValues, fmt.Sprintf("'%v'", string(b)))
					} else if r, ok := value.(driver.Valuer); ok {
						if value, err := r.Value(); err == nil && value != nil {
							formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
						} else {
							formatedValues = append(formatedValues, "NULL")
						}
					} else {
						formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
					}
				} else {
					formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
				}
			}
			messages = append(messages, fmt.Sprintf(sqlRegexp.ReplaceAllString(values[3].(string), "%v"), formatedValues...))
		} else {
			messages = append(messages, "\033[31;1m")
			messages = append(messages, values[2:]...)
			messages = append(messages, "\033[0m")
		}
		logger.Println(messages...)
	}
}
<commit_msg>Disable gorm log.<commit_after>package gorm

import (
	"database/sql/driver"
	"fmt"
	"log"
	"os"
	"reflect"
	"regexp"
	"time"
)

type logger interface {
	Print(v ...interface{})
}

type Logger struct {
	*log.Logger
}

var defaultLogger = Logger{log.New(os.Stdout, "\r\n", 0)}

// Format log
var sqlRegexp = regexp.MustCompile(`(\$\d+)|\?`)

func (logger Logger) Print(values ...interface{}) {

	if gormLog := os.Getenv("GORM_LOG_ON"); gormLog != "" {

		if len(values) > 1 {
			level := values[0]
			currentTime := "\n\033[33m[" + NowFunc().Format("2006-01-02 15:04:05") + "]\033[0m"
			source := fmt.Sprintf("\033[35m(%v)\033[0m", values[1])
			messages := []interface{}{source, currentTime}

			if level == "sql" {
				// duration
				messages = append(messages, fmt.Sprintf(" \033[36;1m[%.2fms]\033[0m ", float64(values[2].(time.Duration).Nanoseconds()/1e4)/100.0))
				// sql
				var formatedValues []interface{}
				for _, value := range values[4].([]interface{}) {
					indirectValue := reflect.Indirect(reflect.ValueOf(value))
					if indirectValue.IsValid() {
						value = indirectValue.Interface()
						if t, ok := value.(time.Time); ok {
							formatedValues = append(formatedValues, fmt.Sprintf("'%v'", t.Format(time.RFC3339)))
						} else if b, ok := value.([]byte); ok {
							formatedValues = append(formatedValues, fmt.Sprintf("'%v'", string(b)))
						} else if r, ok := value.(driver.Valuer); ok {
							if value, err := r.Value(); err == nil && value != nil {
								formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
							} else {
								formatedValues = append(formatedValues, "NULL")
							}
						} else {
							formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
						}
					} else {
						formatedValues = append(formatedValues, fmt.Sprintf("'%v'", value))
					}
				}
				messages = append(messages, fmt.Sprintf(sqlRegexp.ReplaceAllString(values[3].(string), "%v"), formatedValues...))
			} else {
				messages = append(messages, "\033[31;1m")
				messages = append(messages, values[2:]...)
				messages = append(messages, "\033[0m")
			}
			logger.Println(messages...)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The OpenEBS Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package app

import (
	"github.com/openebs/maya/pkg/alertlog"
	"github.com/pkg/errors"
	v1 "k8s.io/api/core/v1"
	"k8s.io/klog"

	pvController "sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
	//pvController "github.com/kubernetes-sigs/sig-storage-lib-external-provisioner/controller"
	mconfig "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
	persistentvolume "github.com/openebs/maya/pkg/kubernetes/persistentvolume/v1alpha1"
)

// ProvisionHostPath is invoked by the Provisioner which expect HostPath PV
//  to be provisioned and a valid PV spec returned.
func (p *Provisioner) ProvisionHostPath(opts pvController.VolumeOptions, volumeConfig *VolumeConfig) (*v1.PersistentVolume, error) {
	pvc := opts.PVC
	nodeHostname := GetNodeHostname(opts.SelectedNode)
	name := opts.PVName
	stgType := volumeConfig.GetStorageType()
	saName := getOpenEBSServiceAccountName()

	path, err := volumeConfig.GetPath()
	if err != nil {
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "Unable to get volume config",
			"storagetype", stgType,
		)
		return nil, err
	}

	klog.Infof("Creating volume %v at %v:%v", name, nodeHostname, path)

	//Before using the path for local PV, make sure it is created.
	initCmdsForPath := []string{"mkdir", "-m", "0777", "-p"}
	podOpts := &HelperPodOptions{
		cmdsForPath:        initCmdsForPath,
		name:               name,
		path:               path,
		nodeHostname:       nodeHostname,
		serviceAccountName: saName,
	}

	iErr := p.createInitPod(podOpts)
	if iErr != nil {
		klog.Infof("Initialize volume %v failed: %v", name, iErr)
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "Volume initialization failed",
			"storagetype", stgType,
		)
		return nil, iErr
	}

	// VolumeMode will always be specified as Filesystem for host path volume,
	// and the value passed in from the PVC spec will be ignored.
	fs := v1.PersistentVolumeFilesystem

	// It is possible that the HostPath doesn't already exist on the node.
	// Set the Local PV to create it.
	//hostPathType := v1.HostPathDirectoryOrCreate

	// TODO initialize the Labels and annotations
	// Use annotations to specify the context using which the PV was created.
	//volAnnotations := make(map[string]string)
	//volAnnotations[string(v1alpha1.CASTypeKey)] = casVolume.Spec.CasType
	//fstype := casVolume.Spec.FSType

	labels := make(map[string]string)
	labels[string(mconfig.CASTypeKey)] = "local-" + stgType
	//labels[string(v1alpha1.StorageClassKey)] = *className

	//TODO Change the following to a builder pattern
	pvObj, err := persistentvolume.NewBuilder().
		WithName(name).
		WithLabels(labels).
		WithReclaimPolicy(opts.PersistentVolumeReclaimPolicy).
		WithAccessModes(pvc.Spec.AccessModes).
		WithVolumeMode(fs).
		WithCapacityQty(pvc.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]).
		WithLocalHostDirectory(path).
		WithNodeAffinity(nodeHostname).
		Build()

	if err != nil {
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "failed to build persistent volume",
			"storagetype", stgType,
		)
		return nil, err
	}
	alertlog.Logger.Infow("",
		"eventcode", "cstor.local.pv.provision.success",
		"msg", "Successfully provisioned CStor Local PV",
		"rname", opts.PVName,
		"storagetype", stgType,
	)
	return pvObj, nil
}

// DeleteHostPath is invoked by the PVC controller to perform clean-up
//  activities before deleteing the PV object. If reclaim policy is
//  set to not-retain, then this function will create a helper pod
//  to delete the host path from the node.
func (p *Provisioner) DeleteHostPath(pv *v1.PersistentVolume) (err error) {
	defer func() {
		err = errors.Wrapf(err, "failed to delete volume %v", pv.Name)
	}()

	//Determine the path and node of the Local PV.
	pvObj := persistentvolume.NewForAPIObject(pv)
	path := pvObj.GetPath()
	if path == "" {
		return errors.Errorf("no HostPath set")
	}

	hostname := pvObj.GetAffinitedNodeHostname()
	if hostname == "" {
		return errors.Errorf("cannot find affinited node hostname")
	}

	//Initiate clean up only when reclaim policy is not retain.
	klog.Infof("Deleting volume %v at %v:%v", pv.Name, hostname, path)
	cleanupCmdsForPath := []string{"rm", "-rf"}
	podOpts := &HelperPodOptions{
		cmdsForPath:  cleanupCmdsForPath,
		name:         pv.Name,
		path:         path,
		nodeHostname: hostname,
	}

	if err := p.createCleanupPod(podOpts); err != nil {
		return errors.Wrapf(err, "clean up volume %v failed", pv.Name)
	}
	return nil
}
<commit_msg>fix(provisioner,localpv): add SA while creating cleanup pod<commit_after>/*
Copyright 2019 The OpenEBS Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package app

import (
	"github.com/openebs/maya/pkg/alertlog"
	"github.com/pkg/errors"
	v1 "k8s.io/api/core/v1"
	"k8s.io/klog"

	pvController "sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
	//pvController "github.com/kubernetes-sigs/sig-storage-lib-external-provisioner/controller"
	mconfig "github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
	persistentvolume "github.com/openebs/maya/pkg/kubernetes/persistentvolume/v1alpha1"
)

// ProvisionHostPath is invoked by the Provisioner which expect HostPath PV
//  to be provisioned and a valid PV spec returned.
func (p *Provisioner) ProvisionHostPath(opts pvController.VolumeOptions, volumeConfig *VolumeConfig) (*v1.PersistentVolume, error) {
	pvc := opts.PVC
	nodeHostname := GetNodeHostname(opts.SelectedNode)
	name := opts.PVName
	stgType := volumeConfig.GetStorageType()
	saName := getOpenEBSServiceAccountName()

	path, err := volumeConfig.GetPath()
	if err != nil {
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "Unable to get volume config",
			"storagetype", stgType,
		)
		return nil, err
	}

	klog.Infof("Creating volume %v at %v:%v", name, nodeHostname, path)

	//Before using the path for local PV, make sure it is created.
	initCmdsForPath := []string{"mkdir", "-m", "0777", "-p"}
	podOpts := &HelperPodOptions{
		cmdsForPath:        initCmdsForPath,
		name:               name,
		path:               path,
		nodeHostname:       nodeHostname,
		serviceAccountName: saName,
	}

	iErr := p.createInitPod(podOpts)
	if iErr != nil {
		klog.Infof("Initialize volume %v failed: %v", name, iErr)
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "Volume initialization failed",
			"storagetype", stgType,
		)
		return nil, iErr
	}

	// VolumeMode will always be specified as Filesystem for host path volume,
	// and the value passed in from the PVC spec will be ignored.
	fs := v1.PersistentVolumeFilesystem

	// It is possible that the HostPath doesn't already exist on the node.
	// Set the Local PV to create it.
	//hostPathType := v1.HostPathDirectoryOrCreate

	// TODO initialize the Labels and annotations
	// Use annotations to specify the context using which the PV was created.
	//volAnnotations := make(map[string]string)
	//volAnnotations[string(v1alpha1.CASTypeKey)] = casVolume.Spec.CasType
	//fstype := casVolume.Spec.FSType

	labels := make(map[string]string)
	labels[string(mconfig.CASTypeKey)] = "local-" + stgType
	//labels[string(v1alpha1.StorageClassKey)] = *className

	//TODO Change the following to a builder pattern
	pvObj, err := persistentvolume.NewBuilder().
		WithName(name).
		WithLabels(labels).
		WithReclaimPolicy(opts.PersistentVolumeReclaimPolicy).
		WithAccessModes(pvc.Spec.AccessModes).
		WithVolumeMode(fs).
		WithCapacityQty(pvc.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]).
		WithLocalHostDirectory(path).
		WithNodeAffinity(nodeHostname).
		Build()

	if err != nil {
		alertlog.Logger.Errorw("",
			"eventcode", "cstor.local.pv.provision.failure",
			"msg", "Failed to provision CStor Local PV",
			"rname", opts.PVName,
			"reason", "failed to build persistent volume",
			"storagetype", stgType,
		)
		return nil, err
	}
	alertlog.Logger.Infow("",
		"eventcode", "cstor.local.pv.provision.success",
		"msg", "Successfully provisioned CStor Local PV",
		"rname", opts.PVName,
		"storagetype", stgType,
	)
	return pvObj, nil
}

// DeleteHostPath is invoked by the PVC controller to perform clean-up
//  activities before deleteing the PV object. If reclaim policy is
//  set to not-retain, then this function will create a helper pod
//  to delete the host path from the node.
func (p *Provisioner) DeleteHostPath(pv *v1.PersistentVolume) (err error) {
	defer func() {
		err = errors.Wrapf(err, "failed to delete volume %v", pv.Name)
	}()

	saName := getOpenEBSServiceAccountName()

	//Determine the path and node of the Local PV.
	pvObj := persistentvolume.NewForAPIObject(pv)
	path := pvObj.GetPath()
	if path == "" {
		return errors.Errorf("no HostPath set")
	}

	hostname := pvObj.GetAffinitedNodeHostname()
	if hostname == "" {
		return errors.Errorf("cannot find affinited node hostname")
	}

	//Initiate clean up only when reclaim policy is not retain.
	klog.Infof("Deleting volume %v at %v:%v", pv.Name, hostname, path)
	cleanupCmdsForPath := []string{"rm", "-rf"}
	podOpts := &HelperPodOptions{
		cmdsForPath:        cleanupCmdsForPath,
		name:               pv.Name,
		path:               path,
		nodeHostname:       hostname,
		serviceAccountName: saName,
	}

	if err := p.createCleanupPod(podOpts); err != nil {
		return errors.Wrapf(err, "clean up volume %v failed", pv.Name)
	}
	return nil
}
<|endoftext|>


<commit_before>package utils

import (
	"database/sql/driver"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"reflect"
	"regexp"
	"runtime"
	"runtime/debug"
	"time"

	"go4.org/sort"

	"github.com/gosimple/slug"
	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/microcosm-cc/bluemonday"
	"github.com/qor/qor"

	"strings"
)

// AppRoot app root path
var AppRoot, _ = os.Getwd()

// ContextKey defined type used for context's key
type ContextKey string

// ContextDBName db name used for context
var ContextDBName ContextKey = "ContextDB"

// HTMLSanitizer html sanitizer to avoid XSS
var HTMLSanitizer = bluemonday.UGCPolicy()

func init() {
	HTMLSanitizer.AllowStandardAttributes()
	if path := os.Getenv("WEB_ROOT"); path != "" {
		AppRoot = path
	}
}

// GetDBFromRequest get database from request
var GetDBFromRequest = func(req *http.Request) *gorm.DB {
	db := req.Context().Value(ContextDBName)
	if tx, ok := db.(*gorm.DB); ok {
		return tx
	}

	return nil
}

// HumanizeString Humanize separates string based on capitalizd letters
// e.g. "OrderItem" -> "Order Item"
func HumanizeString(str string) string {
	var human []rune
	for i, l := range str {
		if i > 0 && isUppercase(byte(l)) {
			if (!isUppercase(str[i-1]) && str[i-1] != ' ') || (i+1 < len(str) && !isUppercase(str[i+1]) && str[i+1] != ' ' && str[i-1] != ' ') {
				human = append(human, rune(' '))
			}
		}
		human = append(human, l)
	}
	return strings.Title(string(human))
}

func isUppercase(char byte) bool {
	return 'A' <= char && char <= 'Z'
}

var asicsiiRegexp = regexp.MustCompile("^(\\w|\\s|-|!)*$")

// ToParamString replaces spaces and separates words (by uppercase letters) with
// underscores in a string, also downcase it
// e.g. ToParamString -> to_param_string, To ParamString -> to_param_string
func ToParamString(str string) string {
	if asicsiiRegexp.MatchString(str) {
		return gorm.ToDBName(strings.Replace(str, " ", "_", -1))
	}
	return slug.Make(str)
}

// PatchURL updates the query part of the request url.
//     PatchURL("google.com","key","value") => "google.com?key=value"
func PatchURL(originalURL string, params ...interface{}) (patchedURL string, err error) {
	url, err := url.Parse(originalURL)
	if err != nil {
		return
	}

	query := url.Query()
	for i := 0; i < len(params)/2; i++ {
		// Check if params is key&value pair
		key := fmt.Sprintf("%v", params[i*2])
		value := fmt.Sprintf("%v", params[i*2+1])

		if value == "" {
			query.Del(key)
		} else {
			query.Set(key, value)
		}
	}

	url.RawQuery = query.Encode()
	patchedURL = url.String()
	return
}

// JoinURL updates the path part of the request url.
//     JoinURL("google.com", "admin") => "google.com/admin"
//     JoinURL("google.com?q=keyword", "admin") => "google.com/admin?q=keyword"
func JoinURL(originalURL string, paths ...interface{}) (joinedURL string, err error) {
	u, err := url.Parse(originalURL)
	if err != nil {
		return
	}

	var urlPaths = []string{u.Path}
	for _, p := range paths {
		urlPaths = append(urlPaths, fmt.Sprint(p))
	}

	if strings.HasSuffix(strings.Join(urlPaths, ""), "/") {
		u.Path = path.Join(urlPaths...) + "/"
	} else {
		u.Path = path.Join(urlPaths...)
	}

	joinedURL = u.String()
	return
}

// SetCookie set cookie for context
func SetCookie(cookie http.Cookie, context *qor.Context) {
	cookie.HttpOnly = true

	// set https cookie
	if context.Request != nil && context.Request.URL.Scheme == "https" {
		cookie.Secure = true
	}

	// set default path
	if cookie.Path == "" {
		cookie.Path = "/"
	}

	http.SetCookie(context.Writer, &cookie)
}

// Stringify stringify any data, if it is a struct, will try to use its Name, Title, Code field, else will use its primary key
func Stringify(object interface{}) string {
	if obj, ok := object.(interface {
		Stringify() string
	}); ok {
		return obj.Stringify()
	}

	scope := gorm.Scope{Value: object}
	for _, column := range []string{"Name", "Title", "Code"} {
		if field, ok := scope.FieldByName(column); ok {
			result := field.Field.Interface()
			if valuer, ok := result.(driver.Valuer); ok {
				if result, err := valuer.Value(); err == nil {
					return fmt.Sprint(result)
				}
			}
			return fmt.Sprint(result)
		}
	}

	if scope.PrimaryField() != nil {
		if scope.PrimaryKeyZero() {
			return ""
		}
		return fmt.Sprintf("%v#%v", scope.GetModelStruct().ModelType.Name(), scope.PrimaryKeyValue())
	}

	return fmt.Sprint(reflect.Indirect(reflect.ValueOf(object)).Interface())
}

// ModelType get value's model type
func ModelType(value interface{}) reflect.Type {
	reflectType := reflect.Indirect(reflect.ValueOf(value)).Type()

	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
		reflectType = reflectType.Elem()
	}

	return reflectType
}

// ParseTagOption parse tag options to hash
func ParseTagOption(str string) map[string]string {
	tags := strings.Split(str, ";")
	setting := map[string]string{}
	for _, value := range tags {
		v := strings.Split(value, ":")
		k := strings.TrimSpace(strings.ToUpper(v[0]))
		if len(v) == 2 {
			setting[k] = v[1]
		} else {
			setting[k] = k
		}
	}
	return setting
}

// ExitWithMsg debug error messages and print stack
func ExitWithMsg(msg interface{}, value ...interface{}) {
	fmt.Printf("\n"+filenameWithLineNum()+"\n"+fmt.Sprint(msg)+"\n", value...)
	debug.PrintStack()
}

// FileServer file server that disabled file listing
func FileServer(dir http.Dir) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		p := path.Join(string(dir), r.URL.Path)
		if f, err := os.Stat(p); err == nil && !f.IsDir() {
			http.ServeFile(w, r, p)
			return
		}

		http.NotFound(w, r)
	})
}

func filenameWithLineNum() string {
	var total = 10
	var results []string
	for i := 2; i < 15; i++ {
		if _, file, line, ok := runtime.Caller(i); ok {
			total--
			results = append(results[:0],
				append(
					[]string{fmt.Sprintf("%v:%v", strings.TrimPrefix(file, os.Getenv("GOPATH")+"src/"), line)},
					results[0:]...)...)

			if total == 0 {
				return strings.Join(results, "\n")
			}
		}
	}
	return ""
}

// GetLocale get locale from request, cookie, after get the locale, will write the locale to the cookie if possible
// Overwrite the default logic with
//     utils.GetLocale = func(context *qor.Context) string {
//         // ....
//     }
var GetLocale = func(context *qor.Context) string {
	if locale := context.Request.Header.Get("Locale"); locale != "" {
		return locale
	}

	if locale := context.Request.URL.Query().Get("locale"); locale != "" {
		if context.Writer != nil {
			context.Request.Header.Set("Locale", locale)
			SetCookie(http.Cookie{Name: "locale", Value: locale, Expires: time.Now().AddDate(1, 0, 0)}, context)
		}
		return locale
	}

	if locale, err := context.Request.Cookie("locale"); err == nil {
		return locale.Value
	}

	return ""
}

// ParseTime parse time from string
// Overwrite the default logic with
//     utils.ParseTime = func(timeStr string, context *qor.Context) (time.Time, error) {
//         // ....
//     }
var ParseTime = func(timeStr string, context *qor.Context) (time.Time, error) {
	return now.Parse(timeStr)
}

// FormatTime format time to string
// Overwrite the default logic with
//     utils.FormatTime = func(time time.Time, format string, context *qor.Context) string {
//         // ....
//     }
var FormatTime = func(date time.Time, format string, context *qor.Context) string {
	return date.Format(format)
}

var replaceIdxRegexp = regexp.MustCompile(`\[\d+\]`)

// SortFormKeys sort form keys
func SortFormKeys(strs []string) {
	sort.Slice(strs, func(i, j int) bool { // true for first
		str1 := strs[i]
		str2 := strs[j]
		matched1 := replaceIdxRegexp.FindAllStringIndex(str1, -1)
		matched2 := replaceIdxRegexp.FindAllStringIndex(str2, -1)

		for x := 0; x < len(matched1); x++ {
			prefix1 := str1[:matched1[x][0]]
			prefix2 := str2

			if len(matched2) >= x+1 {
				prefix2 = str2[:matched2[x][0]]
			}

			if prefix1 != prefix2 {
				return strings.Compare(prefix1, prefix2) < 0
			}

			if len(matched2) < x+1 {
				return false
			}

			number1 := str1[matched1[x][0]:matched1[x][1]]
			number2 := str2[matched2[x][0]:matched2[x][1]]

			if number1 != number2 {
				if len(number1) != len(number2) {
					return len(number1) < len(number2)
				}
				return strings.Compare(number1, number2) < 0
			}
		}

		return strings.Compare(str1, str2) < 0
	})
}

// GetAbsURL get absolute URL from request, refer: https://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server
func GetAbsURL(req *http.Request) url.URL {
	var result url.URL

	if req.URL.IsAbs() {
		return *req.URL
	}

	if domain := req.Header.Get("Origin"); domain != "" {
		parseResult, _ := url.Parse(domain)
		result = *parseResult
	}

	result.Parse(req.RequestURI)
	return result
}
<commit_msg>Get GOPATH from env<commit_after>package utils

import (
	"database/sql/driver"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"reflect"
	"regexp"
	"runtime"
	"runtime/debug"
	"time"

	"go4.org/sort"

	"github.com/gosimple/slug"
	"github.com/jinzhu/gorm"
	"github.com/jinzhu/now"
	"github.com/microcosm-cc/bluemonday"
	"github.com/qor/qor"

	"strings"
)

// AppRoot app root path
var AppRoot, _ = os.Getwd()

// ContextKey defined type used for context's key
type ContextKey string

// ContextDBName db name used for context
var ContextDBName ContextKey = "ContextDB"

// HTMLSanitizer html sanitizer to avoid XSS
var HTMLSanitizer = bluemonday.UGCPolicy()

func init() {
	HTMLSanitizer.AllowStandardAttributes()
	if path := os.Getenv("WEB_ROOT"); path != "" {
		AppRoot = path
	}
}

// GOPATH return GOPATH from env
func GOPATH() []string {
	paths := strings.Split(os.Getenv("GOPATH"), ":")
	if len(paths) == 0 {
		fmt.Println("GOPATH doesn't exist")
	}
	return paths
}

// GetDBFromRequest get database from request
var GetDBFromRequest = func(req *http.Request) *gorm.DB {
	db := req.Context().Value(ContextDBName)
	if tx, ok := db.(*gorm.DB); ok {
		return tx
	}

	return nil
}

// HumanizeString Humanize separates string based on capitalizd letters
// e.g. "OrderItem" -> "Order Item"
func HumanizeString(str string) string {
	var human []rune
	for i, l := range str {
		if i > 0 && isUppercase(byte(l)) {
			if (!isUppercase(str[i-1]) && str[i-1] != ' ') || (i+1 < len(str) && !isUppercase(str[i+1]) && str[i+1] != ' ' && str[i-1] != ' ') {
				human = append(human, rune(' '))
			}
		}
		human = append(human, l)
	}
	return strings.Title(string(human))
}

func isUppercase(char byte) bool {
	return 'A' <= char && char <= 'Z'
}

var asicsiiRegexp = regexp.MustCompile("^(\\w|\\s|-|!)*$")

// ToParamString replaces spaces and separates words (by uppercase letters) with
// underscores in a string, also downcase it
// e.g. ToParamString -> to_param_string, To ParamString -> to_param_string
func ToParamString(str string) string {
	if asicsiiRegexp.MatchString(str) {
		return gorm.ToDBName(strings.Replace(str, " ", "_", -1))
	}
	return slug.Make(str)
}

// PatchURL updates the query part of the request url.
//     PatchURL("google.com","key","value") => "google.com?key=value"
func PatchURL(originalURL string, params ...interface{}) (patchedURL string, err error) {
	url, err := url.Parse(originalURL)
	if err != nil {
		return
	}

	query := url.Query()
	for i := 0; i < len(params)/2; i++ {
		// Check if params is key&value pair
		key := fmt.Sprintf("%v", params[i*2])
		value := fmt.Sprintf("%v", params[i*2+1])

		if value == "" {
			query.Del(key)
		} else {
			query.Set(key, value)
		}
	}

	url.RawQuery = query.Encode()
	patchedURL = url.String()
	return
}

// JoinURL updates the path part of the request url.
//     JoinURL("google.com", "admin") => "google.com/admin"
//     JoinURL("google.com?q=keyword", "admin") => "google.com/admin?q=keyword"
func JoinURL(originalURL string, paths ...interface{}) (joinedURL string, err error) {
	u, err := url.Parse(originalURL)
	if err != nil {
		return
	}

	var urlPaths = []string{u.Path}
	for _, p := range paths {
		urlPaths = append(urlPaths, fmt.Sprint(p))
	}

	if strings.HasSuffix(strings.Join(urlPaths, ""), "/") {
		u.Path = path.Join(urlPaths...) + "/"
	} else {
		u.Path = path.Join(urlPaths...)
	}

	joinedURL = u.String()
	return
}

// SetCookie set cookie for context
func SetCookie(cookie http.Cookie, context *qor.Context) {
	cookie.HttpOnly = true

	// set https cookie
	if context.Request != nil && context.Request.URL.Scheme == "https" {
		cookie.Secure = true
	}

	// set default path
	if cookie.Path == "" {
		cookie.Path = "/"
	}

	http.SetCookie(context.Writer, &cookie)
}

// Stringify stringify any data, if it is a struct, will try to use its Name, Title, Code field, else will use its primary key
func Stringify(object interface{}) string {
	if obj, ok := object.(interface {
		Stringify() string
	}); ok {
		return obj.Stringify()
	}

	scope := gorm.Scope{Value: object}
	for _, column := range []string{"Name", "Title", "Code"} {
		if field, ok := scope.FieldByName(column); ok {
			result := field.Field.Interface()
			if valuer, ok := result.(driver.Valuer); ok {
				if result, err := valuer.Value(); err == nil {
					return fmt.Sprint(result)
				}
			}
			return fmt.Sprint(result)
		}
	}

	if scope.PrimaryField() != nil {
		if scope.PrimaryKeyZero() {
			return ""
		}
		return fmt.Sprintf("%v#%v", scope.GetModelStruct().ModelType.Name(), scope.PrimaryKeyValue())
	}

	return fmt.Sprint(reflect.Indirect(reflect.ValueOf(object)).Interface())
}

// ModelType get value's model type
func ModelType(value interface{}) reflect.Type {
	reflectType := reflect.Indirect(reflect.ValueOf(value)).Type()

	for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
		reflectType = reflectType.Elem()
	}

	return reflectType
}

// ParseTagOption parse tag options to hash
func ParseTagOption(str string) map[string]string {
	tags := strings.Split(str, ";")
	setting := map[string]string{}
	for _, value := range tags {
		v := strings.Split(value, ":")
		k := strings.TrimSpace(strings.ToUpper(v[0]))
		if len(v) == 2 {
			setting[k] = v[1]
		} else {
			setting[k] = k
		}
	}
	return setting
}

// ExitWithMsg debug error messages and print stack
func ExitWithMsg(msg interface{}, value ...interface{}) {
	fmt.Printf("\n"+filenameWithLineNum()+"\n"+fmt.Sprint(msg)+"\n", value...)
	debug.PrintStack()
}

// FileServer file server that disabled file listing
func FileServer(dir http.Dir) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		p := path.Join(string(dir), r.URL.Path)
		if f, err := os.Stat(p); err == nil && !f.IsDir() {
			http.ServeFile(w, r, p)
			return
		}

		http.NotFound(w, r)
	})
}

func filenameWithLineNum() string {
	var total = 10
	var results []string
	for i := 2; i < 15; i++ {
		if _, file, line, ok := runtime.Caller(i); ok {
			total--
			results = append(results[:0],
				append(
					[]string{fmt.Sprintf("%v:%v", strings.TrimPrefix(file, os.Getenv("GOPATH")+"src/"), line)},
					results[0:]...)...)

			if total == 0 {
				return strings.Join(results, "\n")
			}
		}
	}
	return ""
}

// GetLocale get locale from request, cookie, after get the locale, will write the locale to the cookie if possible
// Overwrite the default logic with
//     utils.GetLocale = func(context *qor.Context) string {
//         // ....
//     }
var GetLocale = func(context *qor.Context) string {
	if locale := context.Request.Header.Get("Locale"); locale != "" {
		return locale
	}

	if locale := context.Request.URL.Query().Get("locale"); locale != "" {
		if context.Writer != nil {
			context.Request.Header.Set("Locale", locale)
			SetCookie(http.Cookie{Name: "locale", Value: locale, Expires: time.Now().AddDate(1, 0, 0)}, context)
		}
		return locale
	}

	if locale, err := context.Request.Cookie("locale"); err == nil {
		return locale.Value
	}

	return ""
}

// ParseTime parse time from string
// Overwrite the default logic with
//     utils.ParseTime = func(timeStr string, context *qor.Context) (time.Time, error) {
//         // ....
//     }
var ParseTime = func(timeStr string, context *qor.Context) (time.Time, error) {
	return now.Parse(timeStr)
}

// FormatTime format time to string
// Overwrite the default logic with
//     utils.FormatTime = func(time time.Time, format string, context *qor.Context) string {
//         // ....
//     }
var FormatTime = func(date time.Time, format string, context *qor.Context) string {
	return date.Format(format)
}

var replaceIdxRegexp = regexp.MustCompile(`\[\d+\]`)

// SortFormKeys sort form keys
func SortFormKeys(strs []string) {
	sort.Slice(strs, func(i, j int) bool { // true for first
		str1 := strs[i]
		str2 := strs[j]
		matched1 := replaceIdxRegexp.FindAllStringIndex(str1, -1)
		matched2 := replaceIdxRegexp.FindAllStringIndex(str2, -1)

		for x := 0; x < len(matched1); x++ {
			prefix1 := str1[:matched1[x][0]]
			prefix2 := str2

			if len(matched2) >= x+1 {
				prefix2 = str2[:matched2[x][0]]
			}

			if prefix1 != prefix2 {
				return strings.Compare(prefix1, prefix2) < 0
			}

			if len(matched2) < x+1 {
				return false
			}

			number1 := str1[matched1[x][0]:matched1[x][1]]
			number2 := str2[matched2[x][0]:matched2[x][1]]

			if number1 != number2 {
				if len(number1) != len(number2) {
					return len(number1) < len(number2)
				}
				return strings.Compare(number1, number2) < 0
			}
		}

		return strings.Compare(str1, str2) < 0
	})
}

// GetAbsURL get absolute URL from request, refer: https://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server
func GetAbsURL(req *http.Request) url.URL {
	var result url.URL

	if req.URL.IsAbs() {
		return *req.URL
	}

	if domain := req.Header.Get("Origin"); domain != "" {
		parseResult, _ := url.Parse(domain)
		result = *parseResult
	}

	result.Parse(req.RequestURI)
	return result
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ciliumTest

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"testing"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/test/config"
	. "github.com/cilium/cilium/test/ginkgo-ext"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"

	gops "github.com/google/gops/agent"
	"github.com/onsi/ginkgo"
	ginkgoconfig "github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"
)

var (
	log             = logging.DefaultLogger
	DefaultSettings = map[string]string{
		"K8S_VERSION": "1.10",
	}
	k8sNodesEnv         = "K8S_NODES"
	commandsLogFileName = "cmds.log"
)

func init() {

	// Open socket for using gops to get stacktraces in case the tests deadlock.
	if err := gops.Listen(gops.Options{}); err != nil {
		errorString := fmt.Sprintf("unable to start gops: %s", err)
		fmt.Println(errorString)
		os.Exit(-1)
	}

	for k, v := range DefaultSettings {
		getOrSetEnvVar(k, v)
	}

	config.CiliumTestConfig.ParseFlags()

	os.RemoveAll(helpers.TestResultsPath)
}

func configLogsOutput() {
	log.SetLevel(logrus.DebugLevel)
	log.Out = &config.TestLogWriter
	logrus.SetFormatter(&config.Formatter)
	log.Formatter = &config.Formatter
	log.Hooks.Add(&config.LogHook{})

	ginkgoext.GinkgoWriter = NewWriter(log.Out)
}

func ShowCommands() {
	if !config.CiliumTestConfig.ShowCommands {
		return
	}

	helpers.SSHMetaLogs = ginkgoext.NewWriter(os.Stdout)
}

func TestTest(t *testing.T) {
	configLogsOutput()
	ShowCommands()

	if config.CiliumTestConfig.CiliumDSManifest != "" {
		helpers.CiliumDSPath = config.CiliumTestConfig.CiliumDSManifest
		log.Info("Using new Cilium daemonset manifest '%s'", helpers.CiliumDSPath)
	}

	if config.CiliumTestConfig.HoldEnvironment {
		RegisterFailHandler(helpers.Fail)
	} else {
		RegisterFailHandler(Fail)
	}
	junitReporter := reporters.NewJUnitReporter(fmt.Sprintf(
		"%s.xml", helpers.GetScopeWithVersion()))
	RunSpecsWithDefaultAndCustomReporters(
		t, helpers.GetScopeWithVersion(), []ginkgo.Reporter{junitReporter})
}

func goReportVagrantStatus() chan bool {
	if ginkgoconfig.DefaultReporterConfig.Verbose ||
		ginkgoconfig.DefaultReporterConfig.Succinct {
		// Dev told us they want more/less information than default. Skip.
		return nil
	}

	exit := make(chan bool)
	go func() {
		done := false
		iter := 0
		for {
			var out string
			select {
			case ok := <-exit:
				if ok {
					out = "●\n"
				} else {
					out = "◌\n"
				}
				done = true
			default:
				out = string(rune(int('◜') + iter%4))
			}
			fmt.Printf("\rSpinning up vagrant VMs... %s", out)
			if done {
				return
			}
			time.Sleep(250 * time.Millisecond)
			iter++
		}
	}()
	return exit
}

var _ = BeforeAll(func() {
	var err error

	if !config.CiliumTestConfig.Reprovision {
		// The developer has explicitly told us that they don't care
		// about updating Cilium inside the guest, so skip setup below.
		return
	}

	if config.CiliumTestConfig.SSHConfig != "" {
		// If we set a different VM that it's not in our test environment
		// ginkgo cannot provision it, so skip setup below.
		return
	}

	if progressChan := goReportVagrantStatus(); progressChan != nil {
		defer func() { progressChan <- err == nil }()
	}

	switch helpers.GetScope() {
	case helpers.Runtime:
		err = helpers.CreateVM(helpers.Runtime)
		if err != nil {
			log.WithError(err).Error("Error starting VM")
			Fail(fmt.Sprintf("error starting VM %q: %s", helpers.Runtime, err))
		}

		vm := helpers.InitRuntimeHelper(helpers.Runtime, log.WithFields(
			logrus.Fields{"testName": "BeforeSuite"}))
		err = vm.SetUpCilium()

		if err != nil {
			// AfterFailed function is not defined in this scope, fired the
			// ReportFailed manually for this assert to gather cilium logs Fix
			// #3428
			vm.ReportFailed()
			log.WithError(err).Error("Cilium was unable to be set up correctly")
			Fail(fmt.Sprintf("Cilium was unable to be set up correctly: %s", err))
		}

	case helpers.K8s:
		//FIXME: This should be:
		// Start k8s1 and provision kubernetes.
		// When finish, start to build cilium in background
		// Start k8s2
		// Wait until compilation finished, and pull cilium image on k8s2

		// Name for K8s VMs depends on K8s version that is running.

		err = helpers.CreateVM(helpers.K8s1VMName())
		if err != nil {
			Fail(fmt.Sprintf("error starting VM %q: %s", helpers.K8s1VMName(), err))
		}

		err = helpers.CreateVM(helpers.K8s2VMName())

		if err != nil {
			Fail(fmt.Sprintf("error starting VM %q: %s", helpers.K8s2VMName(), err))
		}
		// For Nightly test we need to have more than two kubernetes nodes. If
		// the env variable K8S_NODES is present, more nodes will be created.
		if nodes := os.Getenv(k8sNodesEnv); nodes != "" {
			nodesInt, err := strconv.Atoi(nodes)
			if err != nil {
				Fail(fmt.Sprintf("%s value is not a number %q", k8sNodesEnv, nodes))
			}
			for i := 3; i <= nodesInt; i++ {
				vmName := fmt.Sprintf("%s%d-%s", helpers.K8s, i, helpers.GetCurrentK8SEnv())
				err = helpers.CreateVM(vmName)
				if err != nil {
					Fail(fmt.Sprintf("error starting VM %q: %s", vmName, err))
				}
			}
		}
	}
	return
})

var _ = AfterAll(func() {
	if !helpers.IsRunningOnJenkins() {
		log.Infof("AfterSuite: not running on Jenkins; leaving VMs running for debugging")
		return
	}

	scope := helpers.GetScope()
	log.Infof("cleaning up VMs started for %s tests", scope)
	switch scope {
	case helpers.Runtime:
		helpers.DestroyVM(helpers.Runtime)
	case helpers.K8s:
		helpers.DestroyVM(helpers.K8s1VMName())
		helpers.DestroyVM(helpers.K8s2VMName())
	}
	return
})

func getOrSetEnvVar(key, value string) {
	if val := os.Getenv(key); val == "" {
		log.Infof("environment variable %q was not set; setting to default value %q", key, value)
		os.Setenv(key, value)
	}
}

var _ = AfterEach(func() {

	defer config.TestLogWriterReset()
	err := helpers.CreateLogFile(config.TestLogFileName, config.TestLogWriter.Bytes())
	if err != nil {
		log.WithError(err).Errorf("cannot create log file '%s'", config.TestLogFileName)
		return
	}

	defer helpers.SSHMetaLogs.Reset()
	err = helpers.CreateLogFile(commandsLogFileName, helpers.SSHMetaLogs.Bytes())
	if err != nil {
		log.WithError(err).Errorf("cannot create log file '%s'", commandsLogFileName)
		return
	}

	// This piece of code is to enable zip attachments on Junit Output.
	if ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
		// ReportDirectory is already created. No check the error
		path, _ := helpers.CreateReportDirectory()
		zipFileName := fmt.Sprintf("%s_%s.zip", helpers.MakeUID(), ginkgoext.GetTestName())
		zipFilePath := filepath.Join(helpers.TestResultsPath, zipFileName)

		_, err := exec.Command(
			"/bin/bash", "-c",
			fmt.Sprintf("zip -qr %s %s", zipFilePath, path)).CombinedOutput()
		if err != nil {
			log.WithError(err).Errorf("cannot create zip file '%s'", zipFilePath)
		}

		ginkgoext.GinkgoPrint("[[ATTACHMENT|%s]]", zipFileName)
	}
})
<commit_msg>test: Be verbose about VM provision failures<commit_after>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ciliumTest

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"testing"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/test/config"
	. "github.com/cilium/cilium/test/ginkgo-ext"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"

	gops "github.com/google/gops/agent"
	"github.com/onsi/ginkgo"
	ginkgoconfig "github.com/onsi/ginkgo/config"
	"github.com/onsi/ginkgo/reporters"
	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"
)

var (
	log             = logging.DefaultLogger
	DefaultSettings = map[string]string{
		"K8S_VERSION": "1.10",
	}
	k8sNodesEnv         = "K8S_NODES"
	commandsLogFileName = "cmds.log"
)

func init() {

	// Open socket for using gops to get stacktraces in case the tests deadlock.
	if err := gops.Listen(gops.Options{}); err != nil {
		errorString := fmt.Sprintf("unable to start gops: %s", err)
		fmt.Println(errorString)
		os.Exit(-1)
	}

	for k, v := range DefaultSettings {
		getOrSetEnvVar(k, v)
	}

	config.CiliumTestConfig.ParseFlags()

	os.RemoveAll(helpers.TestResultsPath)
}

func configLogsOutput() {
	log.SetLevel(logrus.DebugLevel)
	log.Out = &config.TestLogWriter
	logrus.SetFormatter(&config.Formatter)
	log.Formatter = &config.Formatter
	log.Hooks.Add(&config.LogHook{})

	ginkgoext.GinkgoWriter = NewWriter(log.Out)
}

func ShowCommands() {
	if !config.CiliumTestConfig.ShowCommands {
		return
	}

	helpers.SSHMetaLogs = ginkgoext.NewWriter(os.Stdout)
}

func TestTest(t *testing.T) {
	configLogsOutput()
	ShowCommands()

	if config.CiliumTestConfig.CiliumDSManifest != "" {
		helpers.CiliumDSPath = config.CiliumTestConfig.CiliumDSManifest
		log.Info("Using new Cilium daemonset manifest '%s'", helpers.CiliumDSPath)
	}

	if config.CiliumTestConfig.HoldEnvironment {
		RegisterFailHandler(helpers.Fail)
	} else {
		RegisterFailHandler(Fail)
	}
	junitReporter := reporters.NewJUnitReporter(fmt.Sprintf(
		"%s.xml", helpers.GetScopeWithVersion()))
	RunSpecsWithDefaultAndCustomReporters(
		t, helpers.GetScopeWithVersion(), []ginkgo.Reporter{junitReporter})
}

func goReportVagrantStatus() chan bool {
	if ginkgoconfig.DefaultReporterConfig.Verbose ||
		ginkgoconfig.DefaultReporterConfig.Succinct {
		// Dev told us they want more/less information than default. Skip.
		return nil
	}

	exit := make(chan bool)
	go func() {
		done := false
		iter := 0
		for {
			var out string
			select {
			case ok := <-exit:
				if ok {
					out = "●\n"
				} else {
					out = "◌\n"
				}
				done = true
			default:
				out = string(rune(int('◜') + iter%4))
			}
			fmt.Printf("\rSpinning up vagrant VMs... %s", out)
			if done {
				return
			}
			time.Sleep(250 * time.Millisecond)
			iter++
		}
	}()
	return exit
}

func reportCreateVMFailure(vm string, err error) {
	failmsg := fmt.Sprintf(`
        ===================== ERROR - VM PROVISION FAILED =====================

        Unable to provision and start VM %q: %s", vm, err

        =======================================================================
        `, vm, err)
	ginkgoext.GinkgoPrint(failmsg)
	Fail(failmsg)
}

var _ = BeforeAll(func() {
	var err error

	if !config.CiliumTestConfig.Reprovision {
		// The developer has explicitly told us that they don't care
		// about updating Cilium inside the guest, so skip setup below.
		return
	}

	if config.CiliumTestConfig.SSHConfig != "" {
		// If we set a different VM that it's not in our test environment
		// ginkgo cannot provision it, so skip setup below.
		return
	}

	if progressChan := goReportVagrantStatus(); progressChan != nil {
		defer func() { progressChan <- err == nil }()
	}

	switch helpers.GetScope() {
	case helpers.Runtime:
		err = helpers.CreateVM(helpers.Runtime)
		if err != nil {
			log.WithError(err).Error("Error starting VM")
			reportCreateVMFailure(helpers.Runtime, err)
		}

		vm := helpers.InitRuntimeHelper(helpers.Runtime, log.WithFields(
			logrus.Fields{"testName": "BeforeSuite"}))
		err = vm.SetUpCilium()

		if err != nil {
			// AfterFailed function is not defined in this scope, fired the
			// ReportFailed manually for this assert to gather cilium logs Fix
			// #3428
			vm.ReportFailed()
			log.WithError(err).Error("Cilium was unable to be set up correctly")
			reportCreateVMFailure(helpers.Runtime, err)
		}

	case helpers.K8s:
		//FIXME: This should be:
		// Start k8s1 and provision kubernetes.
		// When finish, start to build cilium in background
		// Start k8s2
		// Wait until compilation finished, and pull cilium image on k8s2

		// Name for K8s VMs depends on K8s version that is running.

		err = helpers.CreateVM(helpers.K8s1VMName())
		if err != nil {
			reportCreateVMFailure(helpers.K8s1VMName(), err)
		}

		err = helpers.CreateVM(helpers.K8s2VMName())
		if err != nil {
			reportCreateVMFailure(helpers.K8s2VMName(), err)
		}

		// For Nightly test we need to have more than two kubernetes nodes. If
		// the env variable K8S_NODES is present, more nodes will be created.
		if nodes := os.Getenv(k8sNodesEnv); nodes != "" {
			nodesInt, err := strconv.Atoi(nodes)
			if err != nil {
				Fail(fmt.Sprintf("%s value is not a number %q", k8sNodesEnv, nodes))
			}
			for i := 3; i <= nodesInt; i++ {
				vmName := fmt.Sprintf("%s%d-%s", helpers.K8s, i, helpers.GetCurrentK8SEnv())
				err = helpers.CreateVM(vmName)
				if err != nil {
					reportCreateVMFailure(vmName, err)
				}
			}
		}
	}
	return
})

var _ = AfterAll(func() {
	if !helpers.IsRunningOnJenkins() {
		log.Infof("AfterSuite: not running on Jenkins; leaving VMs running for debugging")
		return
	}

	scope := helpers.GetScope()
	log.Infof("cleaning up VMs started for %s tests", scope)
	switch scope {
	case helpers.Runtime:
		helpers.DestroyVM(helpers.Runtime)
	case helpers.K8s:
		helpers.DestroyVM(helpers.K8s1VMName())
		helpers.DestroyVM(helpers.K8s2VMName())
	}
	return
})

func getOrSetEnvVar(key, value string) {
	if val := os.Getenv(key); val == "" {
		log.Infof("environment variable %q was not set; setting to default value %q", key, value)
		os.Setenv(key, value)
	}
}

var _ = AfterEach(func() {

	defer config.TestLogWriterReset()
	err := helpers.CreateLogFile(config.TestLogFileName, config.TestLogWriter.Bytes())
	if err != nil {
		log.WithError(err).Errorf("cannot create log file '%s'", config.TestLogFileName)
		return
	}

	defer helpers.SSHMetaLogs.Reset()
	err = helpers.CreateLogFile(commandsLogFileName, helpers.SSHMetaLogs.Bytes())
	if err != nil {
		log.WithError(err).Errorf("cannot create log file '%s'", commandsLogFileName)
		return
	}

	// This piece of code is to enable zip attachments on Junit Output.
	if ginkgo.CurrentGinkgoTestDescription().Failed && helpers.IsRunningOnJenkins() {
		// ReportDirectory is already created. No check the error
		path, _ := helpers.CreateReportDirectory()
		zipFileName := fmt.Sprintf("%s_%s.zip", helpers.MakeUID(), ginkgoext.GetTestName())
		zipFilePath := filepath.Join(helpers.TestResultsPath, zipFileName)

		_, err := exec.Command(
			"/bin/bash", "-c",
			fmt.Sprintf("zip -qr %s %s", zipFilePath, path)).CombinedOutput()
		if err != nil {
			log.WithError(err).Errorf("cannot create zip file '%s'", zipFilePath)
		}

		ginkgoext.GinkgoPrint("[[ATTACHMENT|%s]]", zipFileName)
	}
})
<|endoftext|>


<commit_before>package valida

import (
	"fmt"
	"testing"
)

func ExampleOIB() {
	ok := "69435151530"
	wrong := "invalidOIB"
	fmt.Println(OIB(ok))
	fmt.Println(OIB(wrong))
	// Output:
	// true
	// false
}

func TestJMBG(t *testing.T) {
	var tests = []struct {
		in   string
		want bool
	}{
		// valid
		{"0308964384007", true},
		// too short
		{"123", false},
		// invalid
		{"1234567890123", false},
		// not number
		{"123a567b90123", false},
		// last invalid
		{"123456789012a", false},
		// all zeros
		{"0000000000000", false},
		// empty
		{"", false},
	}
	for _, tt := range tests {
		if got := JMBG(tt.in); got != tt.want {
			t.Errorf("JMBG(\"%v\") = %v; want %v",
				tt.in, got, tt.want)
		}
	}
}

func TestOIB(t *testing.T) {
	var tests = []struct {
		in   string
		want bool
	}{
		// valid
		{"69435151530", true},
		// too short
		{"123", false},
		// invalid
		{"12345678901", false},
		// not number
		{"123a5b7c901", false},
		// last invalid
		{"1234567890a", false},
		// all zeros
		{"00000000000", false},
		// empty
		{"", false},
	}
	for _, tt := range tests {
		if got := OIB(tt.in); got != tt.want {
			t.Errorf("OIB(\"%v\") = %v; want %v",
				tt.in, got, tt.want)
		}
	}
}
<commit_msg>Add ExampleJMBG<commit_after>package valida

import (
	"fmt"
	"testing"
)

func ExampleJMBG() {
	ok := "0308964384007"
	wrong := "invalidJMBG"
	fmt.Println(JMBG(ok))
	fmt.Println(JMBG(wrong))
	// Output:
	// true
	// false
}

func ExampleOIB() {
	ok := "69435151530"
	wrong := "invalidOIB"
	fmt.Println(OIB(ok))
	fmt.Println(OIB(wrong))
	// Output:
	// true
	// false
}

func TestJMBG(t *testing.T) {
	var tests = []struct {
		in   string
		want bool
	}{
		// valid
		{"0308964384007", true},
		// too short
		{"123", false},
		// invalid
		{"1234567890123", false},
		// not number
		{"123a567b90123", false},
		// last invalid
		{"123456789012a", false},
		// all zeros
		{"0000000000000", false},
		// empty
		{"", false},
	}
	for _, tt := range tests {
		if got := JMBG(tt.in); got != tt.want {
			t.Errorf("JMBG(\"%v\") = %v; want %v",
				tt.in, got, tt.want)
		}
	}
}

func TestOIB(t *testing.T) {
	var tests = []struct {
		in   string
		want bool
	}{
		// valid
		{"69435151530", true},
		// too short
		{"123", false},
		// invalid
		{"12345678901", false},
		// not number
		{"123a5b7c901", false},
		// last invalid
		{"1234567890a", false},
		// all zeros
		{"00000000000", false},
		// empty
		{"", false},
	}
	for _, tt := range tests {
		if got := OIB(tt.in); got != tt.want {
			t.Errorf("OIB(\"%v\") = %v; want %v",
				tt.in, got, tt.want)
		}
	}
}
<|endoftext|>