double.huang 5 år sedan
incheckning
49e8355a80

+ 17 - 0
.drone.yml

@@ -0,0 +1,17 @@
+clone:
+  depth: 50
+  recursive: true
+  path: light-dauth
+build:
+  image: docker.qianqiusoft.com/golang:1.12
+  commands:
+    - godep
+    - CGO_ENABLED=0 go build -a -ldflags '-s'
+    - tar zcf light-dauth.tar.gz light-dauth
+publish:
+  docker:
+    registry: docker.qianqiusoft.com
+    email: develop@qianqiusoft.com
+    repo: docker.qianqiusoft.com/light-dauth
+    tag:
+      - latest

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+light-dauth
+light-dauth.exe
+.idea

+ 32 - 0
.rancher-pipeline.yml

@@ -0,0 +1,32 @@
+stages:
+  - name: compile
+    steps:
+      - runScriptConfig:
+          image: docker.qianqiusoft.com/library/golang-builder:1.13-alpine
+          shellScript: |-
+            go build -mod=vendor
+            echo "begin tar"
+            tar zcf light-dauth.tar.gz light-dauth
+  - name: build-image
+    steps:
+      - publishImageConfig:
+          dockerfilePath: ./Dockerfile
+          buildContext: .
+          tag: docker.qianqiusoft.com/qianqiusoft/light-dauth:${CICD_GIT_TAG}
+          pushRemote: true
+          registry: docker.qianqiusoft.com
+    when:
+      event:
+        include:
+          - tag
+timeout: 60
+notification:
+  recipients:
+    - recipient: HuangRuiFeng
+      notifier: c-k65bx:n-qxzmv
+    - recipient: ZhangJinQiang
+      notifier: c-k65bx:n-qxzmv
+  condition:
+    - Success
+    - Changed
+    - Failed

+ 8 - 0
Dockerfile

@@ -0,0 +1,8 @@
+FROM docker.qianqiusoft.com/library/alpine:runtime
+
+ADD light-dauth.tar.gz /app/
+
+VOLUME [ "/app/conf" ]
+EXPOSE 8080
+WORKDIR /app
+CMD ["/app/light-dauth"]

+ 10 - 0
IStore.go

@@ -0,0 +1,10 @@
+package main
+
+import (
+)
+
+type IStore interface {
+	Set(key string, bytess []byte)
+	Remove(key string)
+	All()map[string][]byte
+}

+ 444 - 0
conn.go

@@ -0,0 +1,444 @@
+package main
+
+import (
+	"bytes"
+	"context"
+	"errors"
+	"fmt"
+	"git.qianqiusoft.com/library/glog"
+	"git.qianqiusoft.com/qianqiusoft/go-uuid/uuid"
+	"net"
+	"sync"
+	"time"
+)
+
+const (
+	__KEY = "Light#dauth-@*I2"
+
+	CMD_NEW = "new"
+	CMD_REMOVE = "remove"
+	CMD_PINGPONG = "pg"
+	CMD_PINGPONG_RESP = "pg_resp"
+)
+
+/**
+ * 数据包获取接口,跟业务无关
+ */
+type Getter interface {
+	/**
+	 * @brief: 包解析器接口
+	 * @param1 bytess: 当前接收到的数据
+	 * @param2 conn: 当前conn
+	 * @return1: 获取出来包内容,多个
+	 * @return2: 剩余数据
+	 * @return3: 错误信息
+	 */
+	Get(bytess []byte, tcpConn *TcpConn)([][]byte, []byte, error)
+}
+
+/**
+ * 包解析器
+ */
+type Parser interface {
+	/**
+	 * @brief: 包解析器
+	 * @param1 bytess: 包数据
+	 * @param2 tcpConn: 当前连接
+	 */
+	Parse(bytess []byte, tcpConn *TcpConn)
+}
+
+/**
+ * 连接回调接口
+ */
+type TcpConnCallback interface {
+	/**
+	 * @brief: 新连接回调
+	 * @param1 conn: 连接
+	 */
+	OnConnected(conn *TcpConn)               //
+	/**
+	 * @brief: 连接断开回调
+	 * @param1 conn: 连接
+	 * @param2 err: 错误信息,正常关闭的为nil
+	 */
+	OnDisconnected(conn *TcpConn) //
+	/**
+	 * @brief: 错误回调
+	 * @param1 conn: 连接
+	 * @param2 err: 错误信息
+	 */
+	OnError(conn *TcpConn, err error)
+}
+
+/**
+ * tcp 配置信息
+ */
+type Config struct {
+	Ip           string          // ip
+	Port         string          // 端口
+	Network      string          // 默认tcp, The network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
+	Interval     time.Duration   // 心跳间隔
+	Timeout      time.Duration   // 超时时间
+	BufferSize   int             // 接收缓冲区大小
+	SendChanSize int             // 发送通道大小
+	RecvChanSize int             // 接收通道大小
+	Getter       Getter          // tcp 连接处理接口
+	Parser       Parser          // 包解析器
+	ConnCallback TcpConnCallback // 连接回调接口
+}
+
+/**
+ * tcp 连接
+ */
+type TcpConn struct {
+	id         string             // id
+	conn       net.Conn           // 连接
+	sendChan   chan []byte        // 发送数据chan
+	recvChan   chan []byte        // 接收数组chan
+	done       chan bool          // 完成chan
+	ctx        context.Context    // 上下文
+	cancel     context.CancelFunc // 取消函数
+	reqTime    time.Time          // 最后请求时间
+	timer      *time.Ticker       // 超时检查定时器
+	interval   time.Duration      // 心跳时间间隔
+	timeout    time.Duration      // 超时时间
+	bufferSize int                // 接收缓冲区大小
+	tcpServer  *TcpServer         // 服务端
+	Getter     Getter             // 包解析器
+	Parser     Parser             // 包解析器
+	Tag        interface{}        // 连接标识
+}
+
+/**
+ * @brief: 发送接口
+ * @param1 data: 数据
+ */
+func (tc *TcpConn)Send(data []byte){
+	if data == nil{
+		fmt.Println("发送数据为nil")
+		return
+	}
+
+	tc.sendChan <- data
+}
+
+/**
+ * @brief: 取消
+ */
+func (tc *TcpConn)Cancel(){
+	glog.Infoln("tcp conn cancel")
+
+	if tc.timer != nil{
+		tc.timer.Stop()
+	}
+	if tc.cancel != nil{
+		tc.cancel()
+	}
+
+	tc.conn.Close()
+}
+
+/**
+ * @brief: 启动超时
+ */
+func (tc *TcpConn)startTimeoutCheckProcess(){
+	go func() {
+		if int64(tc.interval) <= 0{
+			glog.Infoln("时间间隔小于等于0,不启动超时检测定时器")
+			return
+		}
+
+		tc.timer = time.NewTicker(tc.interval)
+
+		defer func() {
+			tc.timer.Stop()
+			glog.Infoln("startTimeoutCheckProcess end")
+		}()
+
+		for {
+			select {
+			case <-tc.ctx.Done():
+				glog.Infoln("startTimeoutCheckProcess ctx.cancel")
+				return
+			case <-tc.timer.C:
+				now := time.Now()
+				if now.Sub(tc.reqTime) > tc.timeout {
+					glog.Errorln(tc.conn, "heartbeat timeout exit", now.Format("2006-01-02 15:04:05"), tc.reqTime.Format("2006-01-02 15:04:05"))
+					tc.Cancel()
+					return
+				}
+			}
+		}
+	}()
+}
+
+/**
+ * @brief: 发送处理流程
+ */
+func (tc *TcpConn)startSendProcess() {
+	go func() {
+		glog.Infoln(tc.conn, "startSendProcess start")
+		defer func() {
+			tc.done <- true
+			glog.Infoln(tc.conn, "startSendProcess end.")
+		}()
+
+		for {
+			select {
+			case <-tc.ctx.Done():
+				glog.Infoln("startSendProcess ctx.cancel")
+				return
+			case data := <-tc.sendChan:
+				tc.conn.SetWriteDeadline(time.Now().Add(5 * time.Second))
+				if _, err := tc.conn.Write(data); err != nil {
+					glog.Errorln(tc.conn, "conn.Write", err.Error())
+					return
+				}
+			}
+		}
+	}()
+}
+
+/**
+ * @brief: 接收处理流程
+ */
+func (tc *TcpConn)startRecvProcess(){
+	go func() {
+		glog.Infoln(tc.conn, "startRecvProcess start")
+		lastRecvDataTime := time.Now()
+
+		defer func() {
+			glog.Errorln(tc.conn, "last recv time:", lastRecvDataTime.Format("2006-01-02 15:04:05"), "now time:", time.Now().Format("2006-01-02 15:04:05"))
+			tc.done <- true
+			glog.Infoln(tc.conn, "startRecvProcess end.")
+		}()
+
+		ok, err := checkSign(tc.conn)	// 校验
+		if err != nil{
+			fmt.Println("校验key异常, 关闭连接", err.Error())
+			tc.Cancel()
+			return
+		}
+		if !ok{
+			fmt.Println("key错误, 关闭连接", err.Error())
+			tc.Cancel()
+			return
+		}
+		err = writeString(tc.conn, "ok")	// 应答ok
+		if err != nil{
+			fmt.Println("应答ok错误, 关闭连接", err.Error())
+			tc.Cancel()
+			return
+		}
+
+		// 验证成功后直接同步
+		mmap := tc.tcpServer.store.All()
+		for _, v := range mmap{
+			tc.Send(cmdContentToBytes(CMD_NEW, v))
+		}
+
+		for {
+			cmd, err := readString(tc.conn)
+			if err != nil{
+				glog.Errorln(tc.conn, "读取cmd错误:", err.Error())
+				break
+			}
+			if cmd == CMD_NEW{
+				err = tc.newHandler()
+			}else if cmd == CMD_REMOVE{
+				err = tc.removeHandler()
+			} else if cmd == CMD_PINGPONG{
+				err = tc.pingpongHandler()
+			} else{
+				glog.Errorln("未知cmd", CMD_NEW)
+				continue
+			}
+			if err != nil{
+				glog.Errorln(tc.conn, "处理错误:", cmd, err.Error())
+				break
+			}
+		}
+	}()
+}
+
+// 处理新建
+func (tc *TcpConn)newHandler()error{
+	fmt.Println("处理新建")
+	_, err := readUInt32(tc.conn) // 读取主体长度,这个忽略掉
+	if err != nil{
+		glog.Errorln(tc.conn, "读取token错误:", err.Error())
+		return err
+	}
+	tokenStr, err := readString(tc.conn)	// d读取token
+	if err != nil{
+		glog.Errorln(tc.conn, "读取token错误:", err.Error())
+		return err
+	}
+	fmt.Println("处理新建1", tokenStr)
+	tokenBytes, err := readBytes(tc.conn)
+	if err != nil{
+		glog.Errorln(tc.conn, "读取token bytes错误:", err.Error())
+		return err
+	}
+	fmt.Println("处理新建2")
+	fmt.Println(tokenBytes)
+	tc.tcpServer.store.Set(tokenStr, tokenBytes)
+	tc.tcpServer.Broadcast(tc.id, CMD_NEW, tokenBytes)
+	return nil
+}
+
+// 处理移除
+func (tc *TcpConn)removeHandler()error{
+	fmt.Println("处理删除")
+	tokenStr, err := readString(tc.conn)
+	if err != nil{
+		glog.Errorln(tc.conn, "读取token错误:", err.Error())
+		return err
+	}
+	tc.tcpServer.store.Remove(tokenStr)
+	tc.tcpServer.Broadcast(tc.id, CMD_NEW, []byte(tokenStr))
+	return nil
+}
+
+// 处理心跳
+func (tc *TcpConn)pingpongHandler()error{
+	fmt.Println("处理心跳")
+	buf := bytes.NewBuffer([]byte{})
+
+	bytess := []byte(CMD_PINGPONG_RESP)
+	size := len(bytess)
+
+	buf.Write(uint32ToBytes(uint32(size)))
+	buf.Write(bytess)
+
+	tc.Send(buf.Bytes())
+
+	return nil
+}
+
+type TcpServer struct {
+	config  *Config             // 配置
+	connMap sync.Map // 连接列表
+	store   IStore
+}
+
+func NewTcpServer(config *Config, store IStore)*TcpServer{
+	return &TcpServer{
+		config: config,
+		store:  store,
+	}
+}
+
+/**
+ * @brief: 处理新连接
+ * @param1 conn: 新连接
+ * @param2 config: 配置
+ */
+func (ts *TcpServer)handleConn(conn net.Conn, config *Config){
+	ctx, cancel := context.WithCancel(context.Background())
+	tcpConn := &TcpConn{
+		id: uuid.New(),
+		conn:       conn,
+		sendChan:   make(chan []byte, config.SendChanSize),
+		recvChan:   make(chan []byte, config.RecvChanSize),
+		done:       make(chan bool, 1),
+		ctx:        ctx,
+		cancel:     cancel,
+		reqTime:    time.Now(),
+		interval:   config.Interval,
+		timeout:    config.Timeout,
+		bufferSize: ts.config.BufferSize,
+		tcpServer:  ts,
+		Getter:     config.Getter,
+		Parser:     config.Parser,
+		Tag:        nil,
+	}
+
+	ts.connMap.Store(tcpConn.id, tcpConn)
+
+	defer func() {
+		glog.Infoln(tcpConn.conn, "handleConn end -> conn.Close()")
+		err := tcpConn.conn.Close()
+		if err != nil {
+			glog.Errorln(tcpConn.conn, "conn.Close()", err.Error())
+		}
+		glog.Infoln(tcpConn.conn, "handleConn end -> cancel()")
+		tcpConn.cancel()
+	}()
+
+	tcpConn.startSendProcess()
+	tcpConn.startRecvProcess()
+
+	if ts.config.ConnCallback != nil{
+		// 新连接回调
+		ts.config.ConnCallback.OnConnected(tcpConn)
+	}
+
+	<-tcpConn.done
+
+	if ts.config.ConnCallback != nil{
+		// 关闭回调
+		ts.config.ConnCallback.OnDisconnected(tcpConn)
+	}
+
+	ts.connMap.Delete(tcpConn.id)
+}
+
+/**
+ * @brief: 广播
+ * @param1 exceptConnId: 不包含连接kkey
+ * @param2 cmd: 命令
+ * @param3 content: 内容
+ */
+func (ts *TcpServer)Broadcast(exceptConnId, cmd string, content []byte){
+	fmt.Println("广播", exceptConnId, cmd)
+	bytess := cmdContentToBytes(cmd, content)
+
+	ts.connMap.Range(func(k, v interface{})bool{
+		con := v.(*TcpConn)
+		if con.id != exceptConnId{
+			con.Send(bytess)
+		}
+		return true
+	})
+}
+
+/**
+ * @brief: 启动服务端
+ * @param1 config: tcp配置
+ * @return1 错误信息
+ */
+func (ts *TcpServer)StartServer()error{
+	if ts.config == nil{
+		return errors.New("参数config为nil")
+	}
+	glog.Infoln(ts.config)
+
+	network := ts.config.Network
+	if network == ""{
+		network = "tcp"
+	}
+	listen, err := net.Listen(network, ts.config.Ip + ":" + ts.config.Port)
+	if err != nil {
+		glog.Errorln("监听端口失败:", err.Error())
+		return err
+	}
+
+	glog.Infoln("启动dauth服务")
+
+	go func() {
+		for {
+			conn, err := listen.Accept()
+			if err != nil {
+				glog.Errorln("接受TCP连接异常:", err.Error())
+				continue
+			}
+			glog.Infoln("TCP连接来自:", conn.RemoteAddr().String(), conn)
+
+			go ts.handleConn(conn, ts.config)
+		}
+	}()
+
+	return nil
+}

+ 8 - 0
go.mod

@@ -0,0 +1,8 @@
+module light-dauth
+
+go 1.13
+
+require (
+	git.qianqiusoft.com/library/glog v0.0.0-20190618112924-96c02a20b1b4
+	git.qianqiusoft.com/qianqiusoft/go-uuid v0.0.0-20160104082328-2bc5388dd0b3
+)

+ 4 - 0
go.sum

@@ -0,0 +1,4 @@
+git.qianqiusoft.com/library/glog v0.0.0-20190618112924-96c02a20b1b4 h1:M1ns7onzx2/L2TY8KWcVzfTSWua+QOFxFJfZjUq8Oio=
+git.qianqiusoft.com/library/glog v0.0.0-20190618112924-96c02a20b1b4/go.mod h1:2wsPrFy3x5USwQey32nRecuFR11PQJ5HJPU6U10ZUZ4=
+git.qianqiusoft.com/qianqiusoft/go-uuid v0.0.0-20160104082328-2bc5388dd0b3 h1:m/DaTZZwOvVGSHHBDbChys+kIGQhshgL2PM5896CNxQ=
+git.qianqiusoft.com/qianqiusoft/go-uuid v0.0.0-20160104082328-2bc5388dd0b3/go.mod h1:SolI9bNrFdJcnX3582SjOsRGLQOMno5xRkEpexWYutw=

+ 8 - 0
godep.yaml

@@ -0,0 +1,8 @@
+package: ssm_backend
+ignore:
+  - git.qianqiusoft.com/qianqiusoft/light-apiengine
+import:
+  - package: git.qianqiusoft.com/qianqiusoft/go-uuid
+    repo: https://git.qianqiusoft.com/qianqiusoft/go-uuid.git
+  - package: git.qianqiusoft.com/public/glog
+    repo: https://git.qianqiusoft.com/public/glog.git

+ 30 - 0
main.go

@@ -0,0 +1,30 @@
+package main
+
+import (
+	"git.qianqiusoft.com/library/glog"
+)
+func main()  {
+	glog.MaxSize = 1024 * 1024 * 32
+	glog.Infoln("main")
+	glog.SetLevelString("info")
+
+	defer glog.Flush()
+
+	conf := &Config{}
+	conf.Port = "10808"
+	conf.BufferSize = 1024
+	conf.Parser = nil
+	conf.Getter = nil
+
+	store := NewMemoryStore()
+	svr := NewTcpServer(conf, store)
+	store.Svr = svr
+
+	glog.Infoln("启动服务")
+
+	svr.StartServer()
+
+	select {
+
+	}
+}

+ 76 - 0
memory_store.go

@@ -0,0 +1,76 @@
+package main
+
+import (
+	"sync"
+	"time"
+)
+
+type tokenItem struct {
+	Content   []byte
+	Timestamp int64
+}
+
+type MemoryStore struct {
+	Map sync.Map
+	Svr *TcpServer
+}
+
+func NewMemoryStore() *MemoryStore {
+	ms := &MemoryStore{}
+	ms.startRefresh()
+
+	return ms
+}
+
+// 设置
+func (this* MemoryStore)Set(key string, bytess []byte) {
+	this.Map.Store(key, &tokenItem{
+		Content: bytess,
+		Timestamp: time.Now().Unix(),
+	})
+}
+
+// 移除
+func (this* MemoryStore)Remove(key string) {
+	this.Map.Delete(key)
+}
+
+// 所有内容列表
+func (this *MemoryStore)All()map[string][]byte{
+	mmap := make(map[string][]byte)
+	this.Map.Range(func(k, v interface{})bool{
+		ti := v.(*tokenItem)
+		mmap[k.(string)] = ti.Content
+		return true
+	})
+
+	return mmap
+}
+
+// 刷新
+func (this *MemoryStore)startRefresh(){
+	go func() {
+		// 两天检查一下
+		ticket := time.NewTicker(time.Hour * 24 * 30)
+		for {
+			select {
+			case <-ticket.C:
+				keys := make([]string, 0)
+				this.Map.Range(func(k, v interface{}) bool {
+					item := v.(*tokenItem)
+					if time.Now().Sub(time.Unix(item.Timestamp, 0)).Hours() > 24{
+						// 大于两天
+						keys = append(keys, k.(string))
+					}
+					return true
+				})
+				for i := range keys{
+					if _, ok := this.Map.Load(keys[i]); ok{
+						this.Svr.Broadcast("","remove", []byte(keys[i]))
+					}
+					this.Map.Delete(keys[i])
+				}
+			}
+		}
+	}()
+}

+ 201 - 0
util.go

@@ -0,0 +1,201 @@
+package main
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"git.qianqiusoft.com/library/glog"
+	"net"
+	"strconv"
+	"time"
+)
+
+// md5 哈希
+func hash(str string)string{
+	h := md5.New()
+	h.Write([]byte(str))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+// 读取字符串
+func readString(conn net.Conn)(string, error){
+	// 读长度
+	size, err := readUInt32(conn)
+	if err != nil{
+		fmt.Println("读取长度失败,", err.Error())
+		return "", err
+	}
+
+	glog.Infoln("读取长度-", size)
+
+	// 读字符串
+	b := make([]byte, size)
+	n, err := conn.Read(b)
+	if n != int(size){
+		return "", errors.New("读取长度不是" + strconv.Itoa(int(size)))
+	}
+	return string(b), nil
+}
+
+// 写入字符串
+func writeString(conn net.Conn, str string)error{
+	if str == ""{
+		return errors.New("字符串为空")
+	}
+
+	bytess := []byte(str)
+	size := len(bytess)
+
+	// 发送长度
+	err := writeUInt32(conn, uint32(size))
+	if err != nil{
+		fmt.Println("发送长度失败,", err.Error())
+		return err
+	}
+
+	// 发送内容
+	n, err := conn.Write(bytess)
+	if err != nil{
+		fmt.Println("发送内容失败,", err.Error())
+		return err
+	}
+	if n != size{
+		return errors.New("发送长度不是" + strconv.Itoa(int(size)))
+	}
+
+	return nil
+}
+
+// 读取bytes
+func readBytes(conn net.Conn)([]byte, error){
+	// 读长度
+	size, err := readUInt32(conn)
+	if err != nil{
+		fmt.Println("读取长度失败,", err.Error())
+		return nil, err
+	}
+
+	fmt.Println("读取长度", size)
+
+	// 读字符串
+	b := make([]byte, size)
+	n, err := conn.Read(b)
+	if n != int(size){
+		return nil, errors.New("读取长度不是" + strconv.Itoa(int(size)))
+	}
+	return b, nil
+}
+
+// 读取uint64
+func readUInt32(conn net.Conn)(uint32, error){
+	b := make([]byte, 4)
+	n, err := conn.Read(b)
+	if err != nil{
+		fmt.Println("读取长度失败,", err.Error())
+		return 0, err
+	}
+	if n != 4{
+		return 0, errors.New("读取长度不是4")
+	}
+	size := binary.BigEndian.Uint32(b)
+
+	return size, nil
+}
+
+// 读取uint64
+func readUInt64(conn net.Conn)(uint64, error){
+	b := make([]byte, 8)
+	n, err := conn.Read(b)
+	if err != nil{
+		fmt.Println("读取长度失败,", err.Error())
+		return 0, err
+	}
+	if n != 8{
+		return 0, errors.New("读取长度不是8")
+	}
+	size := binary.BigEndian.Uint64(b)
+
+	return size, nil
+}
+
+// 写入长度
+func writeUInt32(conn net.Conn, v uint32)error{
+	// 发送长度
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, v)
+	n, err := conn.Write(b)
+	if err != nil{
+		fmt.Println("发送长度失败,", err.Error())
+		return err
+	}
+	if n != 4{
+		return errors.New("发送长度不是4")
+	}
+
+	return nil
+}
+
+// 转byte数组
+func uint32ToBytes(v uint32) []byte {
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, v)
+
+	return b
+}
+
+// 校验
+func checkSign(conn net.Conn)(bool, error){
+	glog.Infoln("校验")
+	timestamp, err := readUInt64(conn)
+	if err != nil{
+		fmt.Println("读取时间戳错误", err.Error())
+		return false, err
+	}
+	glog.Infoln("校验1")
+	sign, err := readString(conn)
+	if err != nil{
+		fmt.Println("读取sign错误", err.Error())
+		return false, err
+	}
+	seed := strconv.Itoa(int(timestamp)) + __KEY
+	hashVal := hash(seed)
+
+	glog.Infoln("校验2")
+	if hashVal != sign{
+		fmt.Println("校验码错误", hashVal, sign)
+		return false, nil
+	}
+
+	glog.Infoln("校验3")
+	if time.Now().Sub(time.Unix(int64(timestamp) / 1000000000, 0)).Seconds() > 60 {
+		fmt.Println("校验码已失效")
+		return false, nil
+	}
+
+	return  true, nil
+}
+
+// cmd 和 内容转字节数组
+func cmdContentToBytes(cmd string, content []byte)[]byte{
+	buf := bytes.NewBuffer([]byte{})
+
+	// 命令
+	bytess := []byte(cmd)
+	size := len(bytess)
+	b := make([]byte, 4)
+	binary.BigEndian.PutUint32(b, uint32(size))
+	buf.Write(b)
+	buf.Write(bytess)
+
+	// 内容
+	size = len(content)
+	b = make([]byte, 4)
+	binary.BigEndian.PutUint32(b, uint32(size))
+	buf.Write(b)
+	buf.Write(content)
+
+	return buf.Bytes()
+}

+ 191 - 0
vendor/git.qianqiusoft.com/library/glog/LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 34 - 0
vendor/git.qianqiusoft.com/library/glog/README.md

@@ -0,0 +1,34 @@
+glog
+====
+
+在[golang/glog](https://github.com/golang/glog)的基础上做了一些修改。
+
+## 修改的地方:
+1. 增加每天切割日志文件的功能,程序运行时指定 --dailyRolling=true参数即可
+2. 将日志等级由原来的INFO WARN ERROR FATAL改为DEBUG INFO ERROR FATAL
+3. 增加日志输出等级设置,当日志信息等级低于输出等级时则不输出日志信息
+4. 将默认的刷新缓冲区时间由20s改为5s
+
+##使用示例 
+```
+func main() {
+    //初始化命令行参数
+    flag.Parse()
+    //退出时调用,确保日志写入文件中
+    defer glog.Flush()
+    
+    //一般在测试环境下设置输出等级为DEBUG,线上环境设置为INFO
+    glog.SetLevelString("DEBUG") 
+    
+    glog.Info("hello, glog")
+    glog.Warning("warning glog")
+    glog.Error("error glog")
+    
+    glog.Infof("info %d", 1)
+    glog.Warningf("warning %d", 2)
+    glog.Errorf("error %d", 3)
+ }
+ 
+//假设编译后的可执行程序名为demo,运行时指定log_dir参数将日志文件保存到特定的目录
+// ./demo --log_dir=./log --dailyRolling=true 
+```

+ 1211 - 0
vendor/git.qianqiusoft.com/library/glog/glog.go

@@ -0,0 +1,1211 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
+// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
+// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
+//
+// Basic examples:
+//
+//	glog.Info("Prepare to repel boarders")
+//
+//	glog.Fatalf("Initialization failed: %s", err)
+//
+// See the documentation for the V function for an explanation of these examples:
+//
+//	if glog.V(2) {
+//		glog.Info("Starting transaction...")
+//	}
+//
+//	glog.V(2).Infoln("Processed", nItems, "elements")
+//
+// Log output is buffered and written periodically using Flush. Programs
+// should call Flush before exiting to guarantee all log output is written.
+//
+// By default, all log statements write to files in a temporary directory.
+// This package provides several flags that modify this behavior.
+// As a result, flag.Parse must be called before any logging is done.
+//
+//	-logtostderr=false
+//		Logs are written to standard error instead of to files.
+//	-alsologtostderr=false
+//		Logs are written to standard error as well as to files.
+//	-stderrthreshold=ERROR
+//		Log events at or above this severity are logged to standard
+//		error as well as to files.
+//	-log_dir=""
+//		Log files will be written to this directory instead of the
+//		default temporary directory.
+//
+//	Other flags provide aids to debugging.
+//
+//	-log_backtrace_at=""
+//		When set to a file and line number holding a logging statement,
+//		such as
+//			-log_backtrace_at=gopherflakes.go:234
+//		a stack trace will be written to the Info log whenever execution
+//		hits that statement. (Unlike with -vmodule, the ".go" must be
+//		present.)
+//	-v=0
+//		Enable V-leveled logging at the specified level.
+//	-vmodule=""
+//		The syntax of the argument is a comma-separated list of pattern=N,
+//		where pattern is a literal file name (minus the ".go" suffix) or
+//		"glob" pattern and N is a V level. For instance,
+//			-vmodule=gopher*=3
+//		sets the V level to 3 in all Go files whose names begin "gopher".
+//
+package glog
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"io"
+	stdLog "log"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+// severity identifies the sort of log: info, warning etc. It also implements
+// the flag.Value interface. The -stderrthreshold flag is of type severity and
+// should be modified only through the flag.Value interface. The values match
+// the corresponding constants in C++.
+type severity int32 // sync/atomic int32
+var outputSeverity severity
+
+// These constants identify the log levels in order of increasing severity.
+// A message written to a high-severity log file is also written to each
+// lower-severity log file.
+const (
+	debugLog severity = iota
+	infoLog
+	errorLog
+	fatalLog
+	numSeverity = 5
+)
+
+const severityChar = "DIEF"
+
+var severityName = []string{
+	debugLog: "DEBUG",
+	infoLog:  "INFO",
+	errorLog: "ERROR",
+	fatalLog: "FATAL",
+}
+
+//测试tag
+// get returns the value of the severity.
+func (s *severity) get() severity {
+	return severity(atomic.LoadInt32((*int32)(s)))
+}
+
+// set sets the value of the severity.
+func (s *severity) set(val severity) {
+	atomic.StoreInt32((*int32)(s), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (s *severity) String() string {
+	return strconv.FormatInt(int64(*s), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (s *severity) Get() interface{} {
+	return *s
+}
+
+// Set is part of the flag.Value interface.
+func (s *severity) Set(value string) error {
+	var threshold severity
+	// Is it a known name?
+	if v, ok := severityByName(value); ok {
+		threshold = v
+	} else {
+		v, err := strconv.Atoi(value)
+		if err != nil {
+			return err
+		}
+		threshold = severity(v)
+	}
+	logging.stderrThreshold.set(threshold)
+	return nil
+}
+
+func severityByName(s string) (severity, bool) {
+	s = strings.ToUpper(s)
+	for i, name := range severityName {
+		if name == s {
+			return severity(i), true
+		}
+	}
+	return 0, false
+}
+
+func SetLevelString(outputLevel string) {
+	severity, ok := severityByName(outputLevel)
+	if !ok {
+		panic(fmt.Errorf("cannot find severity name %s", outputLevel))
+	}
+	outputSeverity = severity
+}
+
+// OutputStats tracks the number of output lines and bytes written.
+type OutputStats struct {
+	lines int64
+	bytes int64
+}
+
+// Lines returns the number of lines written.
+func (s *OutputStats) Lines() int64 {
+	return atomic.LoadInt64(&s.lines)
+}
+
+// Bytes returns the number of bytes written.
+func (s *OutputStats) Bytes() int64 {
+	return atomic.LoadInt64(&s.bytes)
+}
+
+// Stats tracks the number of lines of output and number of bytes
+// per severity level. Values must be read with atomic.LoadInt64.
+var Stats struct {
+	Debug, Info, Error OutputStats
+}
+
+var severityStats = [numSeverity]*OutputStats{
+	debugLog: &Stats.Debug,
+	infoLog:  &Stats.Info,
+	errorLog: &Stats.Error,
+}
+
+// Level is exported because it appears in the arguments to V and is
+// the type of the v flag, which can be set programmatically.
+// It's a distinct type because we want to discriminate it from logType.
+// Variables of type level are only changed under logging.mu.
+// The -v flag is read only with atomic ops, so the state of the logging
+// module is consistent.
+
+// Level is treated as a sync/atomic int32.
+
+// Level specifies a level of verbosity for V logs. *Level implements
+// flag.Value; the -v flag is of type Level and should be modified
+// only through the flag.Value interface.
+type Level int32
+
+// get returns the value of the Level.
+func (l *Level) get() Level {
+	return Level(atomic.LoadInt32((*int32)(l)))
+}
+
+// set sets the value of the Level.
+func (l *Level) set(val Level) {
+	atomic.StoreInt32((*int32)(l), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (l *Level) String() string {
+	return strconv.FormatInt(int64(*l), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (l *Level) Get() interface{} {
+	return *l
+}
+
+// Set is part of the flag.Value interface.
+func (l *Level) Set(value string) error {
+	v, err := strconv.Atoi(value)
+	if err != nil {
+		return err
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	logging.setVState(Level(v), logging.vmodule.filter, false)
+	return nil
+}
+
+// moduleSpec represents the setting of the -vmodule flag.
+type moduleSpec struct {
+	filter []modulePat
+}
+
+// modulePat contains a filter for the -vmodule flag.
+// It holds a verbosity level and a file pattern to match.
+type modulePat struct {
+	pattern string
+	literal bool // The pattern is a literal string
+	level   Level
+}
+
+// match reports whether the file matches the pattern. It uses a string
+// comparison if the pattern contains no metacharacters.
+func (m *modulePat) match(file string) bool {
+	if m.literal {
+		return file == m.pattern
+	}
+	match, _ := filepath.Match(m.pattern, file)
+	return match
+}
+
+func (m *moduleSpec) String() string {
+	// Lock because the type is not atomic. TODO: clean this up.
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	var b bytes.Buffer
+	for i, f := range m.filter {
+		if i > 0 {
+			b.WriteRune(',')
+		}
+		fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
+	}
+	return b.String()
+}
+
+// Get is part of the (Go 1.2)  flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported.
+func (m *moduleSpec) Get() interface{} {
+	return nil
+}
+
+var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
+
+// Syntax: -vmodule=recordio=2,file=1,gfs*=3
+func (m *moduleSpec) Set(value string) error {
+	var filter []modulePat
+	for _, pat := range strings.Split(value, ",") {
+		if len(pat) == 0 {
+			// Empty strings such as from a trailing comma can be ignored.
+			continue
+		}
+		patLev := strings.Split(pat, "=")
+		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
+			return errVmoduleSyntax
+		}
+		pattern := patLev[0]
+		v, err := strconv.Atoi(patLev[1])
+		if err != nil {
+			return errors.New("syntax error: expect comma-separated list of filename=N")
+		}
+		if v < 0 {
+			return errors.New("negative value for vmodule level")
+		}
+		if v == 0 {
+			continue // Ignore. It's harmless but no point in paying the overhead.
+		}
+		// TODO: check syntax of filter?
+		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	logging.setVState(logging.verbosity, filter, true)
+	return nil
+}
+
+// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
+// that require filepath.Match to be called to match the pattern.
+func isLiteral(pattern string) bool {
+	return !strings.ContainsAny(pattern, `\*?[]`)
+}
+
+// traceLocation represents the setting of the -log_backtrace_at flag.
+type traceLocation struct {
+	file string
+	line int
+}
+
+// isSet reports whether the trace location has been specified.
+// logging.mu is held.
+func (t *traceLocation) isSet() bool {
+	return t.line > 0
+}
+
+// match reports whether the specified file and line matches the trace location.
+// The argument file name is the full path, not the basename specified in the flag.
+// logging.mu is held.
+func (t *traceLocation) match(file string, line int) bool {
+	if t.line != line {
+		return false
+	}
+	if i := strings.LastIndex(file, "/"); i >= 0 {
+		file = file[i+1:]
+	}
+	return t.file == file
+}
+
+func (t *traceLocation) String() string {
+	// Lock because the type is not atomic. TODO: clean this up.
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	return fmt.Sprintf("%s:%d", t.file, t.line)
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported
+func (t *traceLocation) Get() interface{} {
+	return nil
+}
+
+var errTraceSyntax = errors.New("syntax error: expect file.go:234")
+
+// Syntax: -log_backtrace_at=gopherflakes.go:234
+// Note that unlike vmodule the file extension is included here.
+func (t *traceLocation) Set(value string) error {
+	if value == "" {
+		// Unset.
+		t.line = 0
+		t.file = ""
+	}
+	fields := strings.Split(value, ":")
+	if len(fields) != 2 {
+		return errTraceSyntax
+	}
+	file, line := fields[0], fields[1]
+	if !strings.Contains(file, ".") {
+		return errTraceSyntax
+	}
+	v, err := strconv.Atoi(line)
+	if err != nil {
+		return errTraceSyntax
+	}
+	if v <= 0 {
+		return errors.New("negative or zero value for level")
+	}
+	logging.mu.Lock()
+	defer logging.mu.Unlock()
+	t.line = v
+	t.file = file
+	return nil
+}
+
+// flushSyncWriter is the interface satisfied by logging destinations.
+type flushSyncWriter interface {
+	Flush() error
+	Sync() error
+	io.Writer
+}
+
+func init() {
+	flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files")
+	flag.BoolVar(&logging.dailyRolling, "dailyRolling", false, " weather to handle log files daily")
+	flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
+	flag.Var(&logging.verbosity, "v", "log level for V logs")
+	flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
+	flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
+	flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
+
+	// Default stderrThreshold is ERROR.
+	logging.stderrThreshold = errorLog
+
+	//Default outputSeverity is INFO.
+	outputSeverity = infoLog
+
+	logging.setVState(0, nil, false)
+	go logging.flushDaemon()
+}
+
+// Flush flushes all pending log I/O.
+func Flush() {
+	logging.lockAndFlushAll()
+}
+
+// loggingT collects all the global state of the logging setup.
+type loggingT struct {
+	// Boolean flags. Not handled atomically because the flag.Value interface
+	// does not let us avoid the =true, and that shorthand is necessary for
+	// compatibility. TODO: does this matter enough to fix? Seems unlikely.
+	toStderr     bool // The -logtostderr flag.
+	alsoToStderr bool // The -alsologtostderr flag.
+
+	dailyRolling bool
+
+	// Level flag. Handled atomically.
+	stderrThreshold severity // The -stderrthreshold flag.
+
+	// freeList is a list of byte buffers, maintained under freeListMu.
+	freeList *buffer
+	// freeListMu maintains the free list. It is separate from the main mutex
+	// so buffers can be grabbed and printed to without holding the main lock,
+	// for better parallelization.
+	freeListMu sync.Mutex
+
+	// mu protects the remaining elements of this structure and is
+	// used to synchronize logging.
+	mu sync.Mutex
+	// file holds writer for each of the log types.
+	file [numSeverity]flushSyncWriter
+	// pcs is used in V to avoid an allocation when computing the caller's PC.
+	pcs [1]uintptr
+	// vmap is a cache of the V Level for each V() call site, identified by PC.
+	// It is wiped whenever the vmodule flag changes state.
+	vmap map[uintptr]Level
+	// filterLength stores the length of the vmodule filter chain. If greater
+	// than zero, it means vmodule is enabled. It may be read safely
+	// using sync.LoadInt32, but is only modified under mu.
+	filterLength int32
+	// traceLocation is the state of the -log_backtrace_at flag.
+	traceLocation traceLocation
+	// These flags are modified only under lock, although verbosity may be fetched
+	// safely using atomic.LoadInt32.
+	vmodule   moduleSpec // The state of the -vmodule flag.
+	verbosity Level      // V logging level, the value of the -v flag/
+}
+
+// buffer holds a byte Buffer for reuse. The zero value is ready for use.
+type buffer struct {
+	bytes.Buffer
+	tmp  [64]byte // temporary byte array for creating headers.
+	next *buffer
+}
+
+var logging loggingT
+
+// setVState sets a consistent state for V logging.
+// l.mu is held.
+func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
+	// Turn verbosity off so V will not fire while we are in transition.
+	logging.verbosity.set(0)
+	// Ditto for filter length.
+	atomic.StoreInt32(&logging.filterLength, 0)
+
+	// Set the new filters and wipe the pc->Level map if the filter has changed.
+	if setFilter {
+		logging.vmodule.filter = filter
+		logging.vmap = make(map[uintptr]Level)
+	}
+
+	// Things are consistent now, so enable filtering and verbosity.
+	// They are enabled in order opposite to that in V.
+	atomic.StoreInt32(&logging.filterLength, int32(len(filter)))
+	logging.verbosity.set(verbosity)
+}
+
+// getBuffer returns a new, ready-to-use buffer.
+func (l *loggingT) getBuffer() *buffer {
+	l.freeListMu.Lock()
+	b := l.freeList
+	if b != nil {
+		l.freeList = b.next
+	}
+	l.freeListMu.Unlock()
+	if b == nil {
+		b = new(buffer)
+	} else {
+		b.next = nil
+		b.Reset()
+	}
+	return b
+}
+
+// putBuffer returns a buffer to the free list.
+func (l *loggingT) putBuffer(b *buffer) {
+	if b.Len() >= 256 {
+		// Let big buffers die a natural death.
+		return
+	}
+	l.freeListMu.Lock()
+	b.next = l.freeList
+	l.freeList = b
+	l.freeListMu.Unlock()
+}
+
+var timeNow = time.Now // Stubbed out for testing.
+
+/*
+header formats a log header as defined by the C++ implementation.
+It returns a buffer containing the formatted header and the user's file and line number.
+The depth specifies how many stack frames above lives the source line to be identified in the log message.
+
+Log lines have this form:
+	Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+where the fields are defined as follows:
+	L                A single character, representing the log level (eg 'I' for INFO)
+	mm               The month (zero padded; ie May is '05')
+	dd               The day (zero padded)
+	hh:mm:ss.uuuuuu  Time in hours, minutes and fractional seconds
+	threadid         The space-padded thread ID as returned by GetTID()
+	file             The file name
+	line             The line number
+	msg              The user-supplied message
+*/
+func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
+	_, file, line, ok := runtime.Caller(3 + depth)
+	if !ok {
+		file = "???"
+		line = 1
+	} else {
+		slash := strings.LastIndex(file, "/")
+		if slash >= 0 {
+			file = file[slash+1:]
+		}
+	}
+	return l.formatHeader(s, file, line), file, line
+}
+
+// formatHeader formats a log header using the provided file name and line number.
+func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
+	now := timeNow()
+	if line < 0 {
+		line = 0 // not a real line number, but acceptable to someDigits
+	}
+	if s > fatalLog {
+		s = infoLog // for safety.
+	}
+	buf := l.getBuffer()
+
+	// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
+	// It's worth about 3X. Fprintf is hard.
+	_, month, day := now.Date()
+	hour, minute, second := now.Clock()
+	// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+	buf.tmp[0] = severityChar[s]
+	buf.twoDigits(1, int(month))
+	buf.twoDigits(3, day)
+	buf.tmp[5] = ' '
+	buf.twoDigits(6, hour)
+	buf.tmp[8] = ':'
+	buf.twoDigits(9, minute)
+	buf.tmp[11] = ':'
+	buf.twoDigits(12, second)
+	buf.tmp[14] = '.'
+	buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
+	buf.tmp[21] = ' '
+	buf.nDigits(7, 22, pid, ' ') // TODO: should be TID
+	buf.tmp[29] = ' '
+	buf.Write(buf.tmp[:30])
+	buf.WriteString(file)
+	buf.tmp[0] = ':'
+	n := buf.someDigits(1, line)
+	buf.tmp[n+1] = ']'
+	buf.tmp[n+2] = ' '
+	buf.Write(buf.tmp[:n+3])
+	return buf
+}
+
+// Some custom tiny helper functions to print the log header efficiently.
+
+const digits = "0123456789"
+
+// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
+func (buf *buffer) twoDigits(i, d int) {
+	buf.tmp[i+1] = digits[d%10]
+	d /= 10
+	buf.tmp[i] = digits[d%10]
+}
+
+// nDigits formats an n-digit integer at buf.tmp[i],
+// padding with pad on the left.
+// It assumes d >= 0.
+func (buf *buffer) nDigits(n, i, d int, pad byte) {
+	j := n - 1
+	for ; j >= 0 && d > 0; j-- {
+		buf.tmp[i+j] = digits[d%10]
+		d /= 10
+	}
+	for ; j >= 0; j-- {
+		buf.tmp[i+j] = pad
+	}
+}
+
+// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
+func (buf *buffer) someDigits(i, d int) int {
+	// Print into the top, then copy down. We know there's space for at least
+	// a 10-digit number.
+	j := len(buf.tmp)
+	for {
+		j--
+		buf.tmp[j] = digits[d%10]
+		d /= 10
+		if d == 0 {
+			break
+		}
+	}
+	return copy(buf.tmp[i:], buf.tmp[j:])
+}
+
+func (l *loggingT) println(s severity, args ...interface{}) {
+	if s < outputSeverity {
+		return
+	}
+	buf, file, line := l.header(s, 0)
+	fmt.Fprintln(buf, args...)
+	l.output(s, buf, file, line, false)
+}
+
+func (l *loggingT) print(s severity, args ...interface{}) {
+	l.printDepth(s, 1, args...)
+}
+
+func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) {
+	if s < outputSeverity {
+		return
+	}
+	buf, file, line := l.header(s, depth)
+	fmt.Fprint(buf, args...)
+	if buf.Bytes()[buf.Len()-1] != '\n' {
+		buf.WriteByte('\n')
+	}
+	l.output(s, buf, file, line, false)
+}
+
+func (l *loggingT) printf(s severity, format string, args ...interface{}) {
+	if s < outputSeverity {
+		return
+	}
+	buf, file, line := l.header(s, 0)
+	fmt.Fprintf(buf, format, args...)
+	if buf.Bytes()[buf.Len()-1] != '\n' {
+		buf.WriteByte('\n')
+	}
+	l.output(s, buf, file, line, false)
+}
+
+// printWithFileLine behaves like print but uses the provided file and line number.  If
+// alsoLogToStderr is true, the log message always appears on standard error; it
+// will also appear in the log file unless --logtostderr is set.
+func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) {
+	if s < outputSeverity {
+		return
+	}
+	buf := l.formatHeader(s, file, line)
+	fmt.Fprint(buf, args...)
+	if buf.Bytes()[buf.Len()-1] != '\n' {
+		buf.WriteByte('\n')
+	}
+	l.output(s, buf, file, line, alsoToStderr)
+}
+
+// output writes the data to the log files and releases the buffer.
+func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
+	l.mu.Lock()
+	if l.traceLocation.isSet() {
+		if l.traceLocation.match(file, line) {
+			buf.Write(stacks(false))
+		}
+	}
+	data := buf.Bytes()
+	if !flag.Parsed() {
+		os.Stderr.Write([]byte("ERROR: logging before flag.Parse: "))
+		os.Stderr.Write(data)
+	} else if l.toStderr {
+		os.Stderr.Write(data)
+	} else {
+		if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
+			os.Stderr.Write(data)
+		}
+		if l.file[s] == nil {
+			if err := l.createFiles(s); err != nil {
+				os.Stderr.Write(data) // Make sure the message appears somewhere.
+				l.exit(err)
+			}
+		}
+		switch s {
+		case fatalLog:
+			l.file[fatalLog].Write(data)
+			fallthrough
+		case errorLog:
+			l.file[errorLog].Write(data)
+			fallthrough
+		case infoLog:
+			l.file[infoLog].Write(data)
+			fallthrough
+		case debugLog:
+			l.file[debugLog].Write(data)
+		}
+	}
+	if s == fatalLog {
+		// If we got here via Exit rather than Fatal, print no stacks.
+		if atomic.LoadUint32(&fatalNoStacks) > 0 {
+			l.mu.Unlock()
+			timeoutFlush(10 * time.Second)
+			os.Exit(1)
+		}
+		// Dump all goroutine stacks before exiting.
+		// First, make sure we see the trace for the current goroutine on standard error.
+		// If -logtostderr has been specified, the loop below will do that anyway
+		// as the first stack in the full dump.
+		if !l.toStderr {
+			os.Stderr.Write(stacks(false))
+		}
+		// Write the stack trace for all goroutines to the files.
+		trace := stacks(true)
+		logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
+		for log := fatalLog; log >= debugLog; log-- {
+			if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
+				f.Write(trace)
+			}
+		}
+		l.mu.Unlock()
+		timeoutFlush(10 * time.Second)
+		os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
+	}
+	l.putBuffer(buf)
+	l.mu.Unlock()
+	if stats := severityStats[s]; stats != nil {
+		atomic.AddInt64(&stats.lines, 1)
+		atomic.AddInt64(&stats.bytes, int64(len(data)))
+	}
+}
+
+// timeoutFlush calls Flush and returns when it completes or after timeout
+// elapses, whichever happens first.  This is needed because the hooks invoked
+// by Flush may deadlock when glog.Fatal is called from a hook that holds
+// a lock.
+func timeoutFlush(timeout time.Duration) {
+	done := make(chan bool, 1)
+	go func() {
+		Flush() // calls logging.lockAndFlushAll()
+		done <- true
+	}()
+	select {
+	case <-done:
+	case <-time.After(timeout):
+		fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout)
+	}
+}
+
+// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
+func stacks(all bool) []byte {
+	// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
+	n := 10000
+	if all {
+		n = 100000
+	}
+	var trace []byte
+	for i := 0; i < 5; i++ {
+		trace = make([]byte, n)
+		nbytes := runtime.Stack(trace, all)
+		if nbytes < len(trace) {
+			return trace[:nbytes]
+		}
+		n *= 2
+	}
+	return trace
+}
+
+// logExitFunc provides a simple mechanism to override the default behavior
+// of exiting on error. Used in testing and to guarantee we reach a required exit
+// for fatal logs. Instead, exit could be a function rather than a method but that
+// would make its use clumsier.
+var logExitFunc func(error)
+
+// exit is called if there is trouble creating or writing log files.
+// It flushes the logs and exits the program; there's no point in hanging around.
+// l.mu is held.
+func (l *loggingT) exit(err error) {
+	fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err)
+	// If logExitFunc is set, we do that instead of exiting.
+	if logExitFunc != nil {
+		logExitFunc(err)
+		return
+	}
+	l.flushAll()
+	os.Exit(2)
+}
+
+// syncBuffer joins a bufio.Writer to its underlying file, providing access to the
+// file's Sync method and providing a wrapper for the Write method that provides log
+// file rotation. There are conflicting methods, so the file cannot be embedded.
+// l.mu is held for all its methods.
+type syncBuffer struct {
+	logger *loggingT
+	*bufio.Writer
+	file        *os.File
+	sev         severity
+	nbytes      uint64 // The number of bytes written to this file
+	createdDate string
+}
+
+func (sb *syncBuffer) Sync() error {
+	return sb.file.Sync()
+}
+
+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
+	if logging.dailyRolling {
+		if sb.createdDate != string(p[1:5]) {
+			if err := sb.rotateFile(time.Now()); err != nil {
+				sb.logger.exit(err)
+			}
+		}
+	}
+
+	if sb.nbytes+uint64(len(p)) >= MaxSize {
+		if err := sb.rotateFile(time.Now()); err != nil {
+			sb.logger.exit(err)
+		}
+	}
+	n, err = sb.Writer.Write(p)
+	sb.nbytes += uint64(n)
+	if err != nil {
+		sb.logger.exit(err)
+	}
+	return
+}
+
+// rotateFile closes the syncBuffer's file and starts a new one.
+func (sb *syncBuffer) rotateFile(now time.Time) error {
+	if sb.file != nil {
+		sb.Flush()
+		sb.file.Close()
+	}
+	var err error
+	sb.file, _, err = create(severityName[sb.sev], now)
+	sb.nbytes = 0
+	if err != nil {
+		return err
+	}
+
+	sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
+	_, month, day := now.Date()
+	sb.createdDate = fmt.Sprintf("%02d%02d", month, day)
+
+	// Write header.
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
+	fmt.Fprintf(&buf, "Running on machine: %s\n", host)
+	fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH)
+	fmt.Fprintf(&buf, "Log line format: [DIEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n")
+	n, err := sb.file.Write(buf.Bytes())
+	sb.nbytes += uint64(n)
+	return err
+}
+
+// bufferSize sizes the buffer associated with each log file. It's large
+// so that log records can accumulate without the logging thread blocking
+// on disk I/O. The flushDaemon will block instead.
+const bufferSize = 256 * 1024
+
+// createFiles creates all the log files for severity from sev down to infoLog.
+// l.mu is held.
+func (l *loggingT) createFiles(sev severity) error {
+	now := time.Now()
+	// Files are created in decreasing severity order, so as soon as we find one
+	// has already been created, we can stop.
+	for s := sev; s >= debugLog && l.file[s] == nil; s-- {
+		sb := &syncBuffer{
+			logger: l,
+			sev:    s,
+		}
+		if err := sb.rotateFile(now); err != nil {
+			return err
+		}
+		l.file[s] = sb
+	}
+	return nil
+}
+
+var flushInterval time.Duration = 5 * time.Second
+
+// flushDaemon periodically flushes the log file buffers.
+func (l *loggingT) flushDaemon() {
+	for _ = range time.NewTicker(flushInterval).C {
+		l.lockAndFlushAll()
+	}
+}
+
+// lockAndFlushAll is like flushAll but locks l.mu first.
+func (l *loggingT) lockAndFlushAll() {
+	l.mu.Lock()
+	l.flushAll()
+	l.mu.Unlock()
+}
+
+// flushAll flushes all the logs and attempts to "sync" their data to disk.
+// l.mu is held.
+func (l *loggingT) flushAll() {
+	// Flush from fatal down, in case there's trouble flushing.
+	for s := fatalLog; s >= debugLog; s-- {
+		file := l.file[s]
+		if file != nil {
+			file.Flush() // ignore error
+			file.Sync()  // ignore error
+		}
+	}
+}
+
+// CopyStandardLogTo arranges for messages written to the Go "log" package's
+// default logs to also appear in the Google logs for the named and lower
+// severities.  Subsequent changes to the standard log's default output location
+// or format may break this behavior.
+//
+// Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
+// recognized, CopyStandardLogTo panics.
+func CopyStandardLogTo(name string) {
+	sev, ok := severityByName(name)
+	if !ok {
+		panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
+	}
+	// Set a log format that captures the user's file and line:
+	//   d.go:23: message
+	stdLog.SetFlags(stdLog.Lshortfile)
+	stdLog.SetOutput(logBridge(sev))
+}
+
+// logBridge provides the Write method that enables CopyStandardLogTo to connect
+// Go's standard logs to the logs provided by this package.
+type logBridge severity
+
+// Write parses the standard logging line and passes its components to the
+// logger for severity(lb).
+func (lb logBridge) Write(b []byte) (n int, err error) {
+	var (
+		file = "???"
+		line = 1
+		text string
+	)
+	// Split "d.go:23: message" into "d.go", "23", and "message".
+	if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
+		text = fmt.Sprintf("bad log format: %s", b)
+	} else {
+		file = string(parts[0])
+		text = string(parts[2][1:]) // skip leading space
+		line, err = strconv.Atoi(string(parts[1]))
+		if err != nil {
+			text = fmt.Sprintf("bad line number: %s", b)
+			line = 1
+		}
+	}
+	// printWithFileLine with alsoToStderr=true, so standard log messages
+	// always appear on standard error.
+	logging.printWithFileLine(severity(lb), file, line, true, text)
+	return len(b), nil
+}
+
+// setV computes and remembers the V level for a given PC
+// when vmodule is enabled.
+// File pattern matching takes the basename of the file, stripped
+// of its .go suffix, and uses filepath.Match, which is a little more
+// general than the *? matching used in C++.
+// l.mu is held.
+func (l *loggingT) setV(pc uintptr) Level {
+	fn := runtime.FuncForPC(pc)
+	file, _ := fn.FileLine(pc)
+	// The file is something like /a/b/c/d.go. We want just the d.
+	if strings.HasSuffix(file, ".go") {
+		file = file[:len(file)-3]
+	}
+	if slash := strings.LastIndex(file, "/"); slash >= 0 {
+		file = file[slash+1:]
+	}
+	for _, filter := range l.vmodule.filter {
+		if filter.match(file) {
+			l.vmap[pc] = filter.level
+			return filter.level
+		}
+	}
+	l.vmap[pc] = 0
+	return 0
+}
+
+// Verbose is a boolean type that implements Infof (like Printf) etc.
+// See the documentation of V for more information.
+type Verbose bool
+
+// V reports whether verbosity at the call site is at least the requested level.
+// The returned value is a boolean of type Verbose, which implements Info, Infoln
+// and Infof. These methods will write to the Info log if called.
+// Thus, one may write either
+//	if glog.V(2) { glog.Info("log this") }
+// or
+//	glog.V(2).Info("log this")
+// The second form is shorter but the first is cheaper if logging is off because it does
+// not evaluate its arguments.
+//
+// Whether an individual call to V generates a log record depends on the setting of
+// the -v and --vmodule flags; both are off by default. If the level in the call to
+// V is at least the value of -v, or of -vmodule for the source file containing the
+// call, the V call will log.
+func V(level Level) Verbose {
+	// This function tries hard to be cheap unless there's work to do.
+	// The fast path is two atomic loads and compares.
+
+	// Here is a cheap but safe test to see if V logging is enabled globally.
+	if logging.verbosity.get() >= level {
+		return Verbose(true)
+	}
+
+	// It's off globally but it vmodule may still be set.
+	// Here is another cheap but safe test to see if vmodule is enabled.
+	if atomic.LoadInt32(&logging.filterLength) > 0 {
+		// Now we need a proper lock to use the logging structure. The pcs field
+		// is shared so we must lock before accessing it. This is fairly expensive,
+		// but if V logging is enabled we're slow anyway.
+		logging.mu.Lock()
+		defer logging.mu.Unlock()
+		if runtime.Callers(2, logging.pcs[:]) == 0 {
+			return Verbose(false)
+		}
+		v, ok := logging.vmap[logging.pcs[0]]
+		if !ok {
+			v = logging.setV(logging.pcs[0])
+		}
+		return Verbose(v >= level)
+	}
+	return Verbose(false)
+}
+
+// Info is equivalent to the global Info function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Info(args ...interface{}) {
+	if v {
+		logging.print(infoLog, args...)
+	}
+}
+
+// Infoln is equivalent to the global Infoln function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infoln(args ...interface{}) {
+	if v {
+		logging.println(infoLog, args...)
+	}
+}
+
+// Infof is equivalent to the global Infof function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infof(format string, args ...interface{}) {
+	if v {
+		logging.printf(infoLog, format, args...)
+	}
+}
+
+func Debug(args ...interface{}) {
+	logging.print(debugLog, args...)
+}
+
+func DebugDepth(depth int, args ...interface{}) {
+	logging.printDepth(debugLog, depth, args...)
+}
+
+func Debugln(args ...interface{}) {
+	logging.println(debugLog, args...)
+}
+
+func Debugf(format string, args ...interface{}) {
+	logging.printf(debugLog, format, args...)
+}
+
+// Info logs to the INFO log.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Info(args ...interface{}) {
+	logging.print(infoLog, args...)
+}
+
+// InfoDepth acts as Info but uses depth to determine which call frame to log.
+// InfoDepth(0, "msg") is the same as Info("msg").
+func InfoDepth(depth int, args ...interface{}) {
+	logging.printDepth(infoLog, depth, args...)
+}
+
+// Infoln logs to the INFO log.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Infoln(args ...interface{}) {
+	logging.println(infoLog, args...)
+}
+
+// Infof logs to the INFO log.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Infof(format string, args ...interface{}) {
+	logging.printf(infoLog, format, args...)
+}
+
+// Error logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Error(args ...interface{}) {
+	logging.print(errorLog, args...)
+}
+
+// ErrorDepth acts as Error but uses depth to determine which call frame to log.
+// ErrorDepth(0, "msg") is the same as Error("msg").
+func ErrorDepth(depth int, args ...interface{}) {
+	logging.printDepth(errorLog, depth, args...)
+}
+
+// Errorln logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Errorln(args ...interface{}) {
+	logging.println(errorLog, args...)
+}
+
+// Errorf logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Errorf(format string, args ...interface{}) {
+	logging.printf(errorLog, format, args...)
+}
+
+// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Fatal(args ...interface{}) {
+	logging.print(fatalLog, args...)
+}
+
+// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
+// FatalDepth(0, "msg") is the same as Fatal("msg").
+func FatalDepth(depth int, args ...interface{}) {
+	logging.printDepth(fatalLog, depth, args...)
+}
+
+// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Fatalln(args ...interface{}) {
+	logging.println(fatalLog, args...)
+}
+
+// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Fatalf(format string, args ...interface{}) {
+	logging.printf(fatalLog, format, args...)
+}
+
+// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
+// It allows Exit and relatives to use the Fatal logs.
+var fatalNoStacks uint32
+
+// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Exit(args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.print(fatalLog, args...)
+}
+
+// ExitDepth acts as Exit but uses depth to determine which call frame to log.
+// ExitDepth(0, "msg") is the same as Exit("msg").
+func ExitDepth(depth int, args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.printDepth(fatalLog, depth, args...)
+}
+
+// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+func Exitln(args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.println(fatalLog, args...)
+}
+
+// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Exitf(format string, args ...interface{}) {
+	atomic.StoreUint32(&fatalNoStacks, 1)
+	logging.printf(fatalLog, format, args...)
+}

+ 124 - 0
vendor/git.qianqiusoft.com/library/glog/glog_file.go

@@ -0,0 +1,124 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// File I/O for logs.
+
+package glog
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"os"
+	"os/user"
+	"path/filepath"
+	"strings"
+	"sync"
+	"time"
+)
+
+// MaxSize is the maximum size of a log file in bytes.
+var MaxSize uint64 = 1024 * 1024 * 1800
+
+// logDirs lists the candidate directories for new log files.
+var logDirs []string
+
+// If non-empty, overrides the choice of directory in which to write logs.
+// See createLogDirs for the full list of possible destinations.
+var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
+
+func createLogDirs() {
+	if *logDir != "" {
+		logDirs = append(logDirs, *logDir)
+	}
+	logDirs = append(logDirs, os.TempDir())
+}
+
+var (
+	pid      = os.Getpid()
+	program  = filepath.Base(os.Args[0])
+	host     = "unknownhost"
+	userName = "unknownuser"
+)
+
+func init() {
+	h, err := os.Hostname()
+	if err == nil {
+		host = shortHostname(h)
+	}
+
+	current, err := user.Current()
+	if err == nil {
+		userName = current.Username
+	}
+
+	// Sanitize userName since it may contain filepath separators on Windows.
+	userName = strings.Replace(userName, `\`, "_", -1)
+}
+
+// shortHostname returns its argument, truncating at the first period.
+// For instance, given "www.google.com" it returns "www".
+func shortHostname(hostname string) string {
+	if i := strings.Index(hostname, "."); i >= 0 {
+		return hostname[:i]
+	}
+	return hostname
+}
+
+// logName returns a new log file name containing tag, with start time t, and
+// the name for the symlink for tag.
+func logName(tag string, t time.Time) (name, link string) {
+	name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
+		program,
+		host,
+		userName,
+		tag,
+		t.Year(),
+		t.Month(),
+		t.Day(),
+		t.Hour(),
+		t.Minute(),
+		t.Second(),
+		pid)
+	return name, program + "." + tag
+}
+
+var onceLogDirs sync.Once
+
+// create creates a new log file and returns the file and its filename, which
+// contains tag ("INFO", "FATAL", etc.) and t.  If the file is created
+// successfully, create also attempts to update the symlink for that tag, ignoring
+// errors.
+func create(tag string, t time.Time) (f *os.File, filename string, err error) {
+	onceLogDirs.Do(createLogDirs)
+	if len(logDirs) == 0 {
+		return nil, "", errors.New("log: no log dirs")
+	}
+	name, link := logName(tag, t)
+	var lastErr error
+	for _, dir := range logDirs {
+		fname := filepath.Join(dir, name)
+		f, err := os.Create(fname)
+		if err == nil {
+			symlink := filepath.Join(dir, link)
+			os.Remove(symlink)        // ignore err
+			os.Symlink(name, symlink) // ignore err
+			return f, fname, nil
+		}
+		lastErr = err
+	}
+	return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
+}

+ 1 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/CONTRIBUTORS

@@ -0,0 +1 @@
+Paul Borman <borman@google.com>

+ 27 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2009 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 84 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/dce.go

@@ -0,0 +1,84 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+	"fmt"
+	"os"
+)
+
+// A Domain represents a Version 2 domain
+type Domain byte
+
+// Domain constants for DCE Security (Version 2) UUIDs.
+const (
+	Person = Domain(0)
+	Group  = Domain(1)
+	Org    = Domain(2)
+)
+
+// NewDCESecurity returns a DCE Security (Version 2) UUID.
+//
+// The domain should be one of Person, Group or Org.
+// On a POSIX system the id should be the users UID for the Person
+// domain and the users GID for the Group.  The meaning of id for
+// the domain Org or on non-POSIX systems is site defined.
+//
+// For a given domain/id pair the same token may be returned for up to
+// 7 minutes and 10 seconds.
+func NewDCESecurity(domain Domain, id uint32) UUID {
+	uuid := NewUUID()
+	if uuid != nil {
+		uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
+		uuid[9] = byte(domain)
+		binary.BigEndian.PutUint32(uuid[0:], id)
+	}
+	return uuid
+}
+
+// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
+// domain with the id returned by os.Getuid.
+//
+//  NewDCEPerson(Person, uint32(os.Getuid()))
+func NewDCEPerson() UUID {
+	return NewDCESecurity(Person, uint32(os.Getuid()))
+}
+
+// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
+// domain with the id returned by os.Getgid.
+//
+//  NewDCEGroup(Group, uint32(os.Getgid()))
+func NewDCEGroup() UUID {
+	return NewDCESecurity(Group, uint32(os.Getgid()))
+}
+
+// Domain returns the domain for a Version 2 UUID or false.
+func (uuid UUID) Domain() (Domain, bool) {
+	if v, _ := uuid.Version(); v != 2 {
+		return 0, false
+	}
+	return Domain(uuid[9]), true
+}
+
+// Id returns the id for a Version 2 UUID or false.
+func (uuid UUID) Id() (uint32, bool) {
+	if v, _ := uuid.Version(); v != 2 {
+		return 0, false
+	}
+	return binary.BigEndian.Uint32(uuid[0:4]), true
+}
+
+func (d Domain) String() string {
+	switch d {
+	case Person:
+		return "Person"
+	case Group:
+		return "Group"
+	case Org:
+		return "Org"
+	}
+	return fmt.Sprintf("Domain%d", int(d))
+}

+ 8 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/doc.go

@@ -0,0 +1,8 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The uuid package generates and inspects UUIDs.
+//
+// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
+package uuid

+ 53 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/hash.go

@@ -0,0 +1,53 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"crypto/md5"
+	"crypto/sha1"
+	"hash"
+)
+
+// Well known Name Space IDs and UUIDs
+var (
+	NameSpace_DNS  = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
+	NameSpace_URL  = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
+	NameSpace_OID  = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
+	NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
+	NIL            = Parse("00000000-0000-0000-0000-000000000000")
+)
+
+// NewHash returns a new UUID dervied from the hash of space concatenated with
+// data generated by h.  The hash should be at least 16 byte in length.  The
+// first 16 bytes of the hash are used to form the UUID.  The version of the
+// UUID will be the lower 4 bits of version.  NewHash is used to implement
+// NewMD5 and NewSHA1.
+func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
+	h.Reset()
+	h.Write(space)
+	h.Write([]byte(data))
+	s := h.Sum(nil)
+	uuid := make([]byte, 16)
+	copy(uuid, s)
+	uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
+	uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
+	return uuid
+}
+
+// NewMD5 returns a new MD5 (Version 3) UUID based on the
+// supplied name space and data.
+//
+//  NewHash(md5.New(), space, data, 3)
+func NewMD5(space UUID, data []byte) UUID {
+	return NewHash(md5.New(), space, data, 3)
+}
+
+// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
+// supplied name space and data.
+//
+//  NewHash(sha1.New(), space, data, 5)
+func NewSHA1(space UUID, data []byte) UUID {
+	return NewHash(sha1.New(), space, data, 5)
+}

+ 101 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/node.go

@@ -0,0 +1,101 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "net"
+
+var (
+	interfaces []net.Interface // cached list of interfaces
+	ifname     string          // name of interface being used
+	nodeID     []byte          // hardware for version 1 UUIDs
+)
+
+// NodeInterface returns the name of the interface from which the NodeID was
+// derived.  The interface "user" is returned if the NodeID was set by
+// SetNodeID.
+func NodeInterface() string {
+	return ifname
+}
+
+// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
+// If name is "" then the first usable interface found will be used or a random
+// Node ID will be generated.  If a named interface cannot be found then false
+// is returned.
+//
+// SetNodeInterface never fails when name is "".
+func SetNodeInterface(name string) bool {
+	if interfaces == nil {
+		var err error
+		interfaces, err = net.Interfaces()
+		if err != nil && name != "" {
+			return false
+		}
+	}
+
+	for _, ifs := range interfaces {
+		if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
+			if setNodeID(ifs.HardwareAddr) {
+				ifname = ifs.Name
+				return true
+			}
+		}
+	}
+
+	// We found no interfaces with a valid hardware address.  If name
+	// does not specify a specific interface generate a random Node ID
+	// (section 4.1.6)
+	if name == "" {
+		if nodeID == nil {
+			nodeID = make([]byte, 6)
+		}
+		randomBits(nodeID)
+		return true
+	}
+	return false
+}
+
+// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
+// if not already set.
+func NodeID() []byte {
+	if nodeID == nil {
+		SetNodeInterface("")
+	}
+	nid := make([]byte, 6)
+	copy(nid, nodeID)
+	return nid
+}
+
+// SetNodeID sets the Node ID to be used for Version 1 UUIDs.  The first 6 bytes
+// of id are used.  If id is less than 6 bytes then false is returned and the
+// Node ID is not set.
+func SetNodeID(id []byte) bool {
+	if setNodeID(id) {
+		ifname = "user"
+		return true
+	}
+	return false
+}
+
+func setNodeID(id []byte) bool {
+	if len(id) < 6 {
+		return false
+	}
+	if nodeID == nil {
+		nodeID = make([]byte, 6)
+	}
+	copy(nodeID, id)
+	return true
+}
+
+// NodeID returns the 6 byte node id encoded in uuid.  It returns nil if uuid is
+// not valid.  The NodeID is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) NodeID() []byte {
+	if len(uuid) != 16 {
+		return nil
+	}
+	node := make([]byte, 6)
+	copy(node, uuid[10:])
+	return node
+}

+ 112 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/time.go

@@ -0,0 +1,112 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+	"time"
+)
+
+// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
+// 1582.
+type Time int64
+
+const (
+	lillian    = 2299160          // Julian day of 15 Oct 1582
+	unix       = 2440587          // Julian day of 1 Jan 1970
+	epoch      = unix - lillian   // Days between epochs
+	g1582      = epoch * 86400    // seconds between epochs
+	g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
+)
+
+var (
+	lasttime  uint64 // last time we returned
+	clock_seq uint16 // clock sequence for this run
+
+	timeNow = time.Now // for testing
+)
+
+// UnixTime converts t the number of seconds and nanoseconds using the Unix
+// epoch of 1 Jan 1970.
+func (t Time) UnixTime() (sec, nsec int64) {
+	sec = int64(t - g1582ns100)
+	nsec = (sec % 10000000) * 100
+	sec /= 10000000
+	return sec, nsec
+}
+
+// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
+// adjusts the clock sequence as needed.  An error is returned if the current
+// time cannot be determined.
+func GetTime() (Time, error) {
+	t := timeNow()
+
+	// If we don't have a clock sequence already, set one.
+	if clock_seq == 0 {
+		SetClockSequence(-1)
+	}
+	now := uint64(t.UnixNano()/100) + g1582ns100
+
+	// If time has gone backwards with this clock sequence then we
+	// increment the clock sequence
+	if now <= lasttime {
+		clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
+	}
+	lasttime = now
+	return Time(now), nil
+}
+
+// ClockSequence returns the current clock sequence, generating one if not
+// already set.  The clock sequence is only used for Version 1 UUIDs.
+//
+// The uuid package does not use global static storage for the clock sequence or
+// the last time a UUID was generated.  Unless SetClockSequence a new random
+// clock sequence is generated the first time a clock sequence is requested by
+// ClockSequence, GetTime, or NewUUID.  (section 4.2.1.1) sequence is generated
+// for
+func ClockSequence() int {
+	if clock_seq == 0 {
+		SetClockSequence(-1)
+	}
+	return int(clock_seq & 0x3fff)
+}
+
+// SetClockSeq sets the clock sequence to the lower 14 bits of seq.  Setting to
+// -1 causes a new sequence to be generated.
+func SetClockSequence(seq int) {
+	if seq == -1 {
+		var b [2]byte
+		randomBits(b[:]) // clock sequence
+		seq = int(b[0])<<8 | int(b[1])
+	}
+	old_seq := clock_seq
+	clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
+	if old_seq != clock_seq {
+		lasttime = 0
+	}
+}
+
+// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
+// uuid.  It returns false if uuid is not valid.  The time is only well defined
+// for version 1 and 2 UUIDs.
+func (uuid UUID) Time() (Time, bool) {
+	if len(uuid) != 16 {
+		return 0, false
+	}
+	time := int64(binary.BigEndian.Uint32(uuid[0:4]))
+	time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
+	time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
+	return Time(time), true
+}
+
+// ClockSequence returns the clock sequence encoded in uuid.  It returns false
+// if uuid is not valid.  The clock sequence is only well defined for version 1
+// and 2 UUIDs.
+func (uuid UUID) ClockSequence() (int, bool) {
+	if len(uuid) != 16 {
+		return 0, false
+	}
+	return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
+}

+ 43 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/util.go

@@ -0,0 +1,43 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"io"
+)
+
+// randomBits completely fills slice b with random data.
+func randomBits(b []byte) {
+	if _, err := io.ReadFull(rander, b); err != nil {
+		panic(err.Error()) // rand should never fail
+	}
+}
+
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = []byte{
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// xtob converts the the first two hex bytes of x into a byte.
+func xtob(x string) (byte, bool) {
+	b1 := xvalues[x[0]]
+	b2 := xvalues[x[1]]
+	return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}

+ 163 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/uuid.go

@@ -0,0 +1,163 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
+// 4122.
+type UUID []byte
+
+// A Version represents a UUIDs version.
+type Version byte
+
+// A Variant represents a UUIDs variant.
+type Variant byte
+
+// Constants returned by Variant.
+const (
+	Invalid   = Variant(iota) // Invalid UUID
+	RFC4122                   // The variant specified in RFC4122
+	Reserved                  // Reserved, NCS backward compatibility.
+	Microsoft                 // Reserved, Microsoft Corporation backward compatibility.
+	Future                    // Reserved for future definition.
+)
+
+var rander = rand.Reader // random function
+
+// New returns a new random (version 4) UUID as a string.  It is a convenience
+// function for NewRandom().String().
+func New() string {
+	return NewRandom().String()
+}
+
+// Parse decodes s into a UUID or returns nil.  Both the UUID form of
+// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
+func Parse(s string) UUID {
+	if len(s) == 36+9 {
+		if strings.ToLower(s[:9]) != "urn:uuid:" {
+			return nil
+		}
+		s = s[9:]
+	} else if len(s) != 36 {
+		return nil
+	}
+	if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+		return nil
+	}
+	uuid := make([]byte, 16)
+	for i, x := range []int{
+		0, 2, 4, 6,
+		9, 11,
+		14, 16,
+		19, 21,
+		24, 26, 28, 30, 32, 34} {
+		if v, ok := xtob(s[x:]); !ok {
+			return nil
+		} else {
+			uuid[i] = v
+		}
+	}
+	return uuid
+}
+
+// Equal returns true if uuid1 and uuid2 are equal.
+func Equal(uuid1, uuid2 UUID) bool {
+	return bytes.Equal(uuid1, uuid2)
+}
+
+// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+// , or "" if uuid is invalid.
+func (uuid UUID) String() string {
+	if uuid == nil || len(uuid) != 16 {
+		return ""
+	}
+	b := []byte(uuid)
+	return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
+		b[:4], b[4:6], b[6:8], b[8:10], b[10:])
+}
+
+// URN returns the RFC 2141 URN form of uuid,
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,  or "" if uuid is invalid.
+func (uuid UUID) URN() string {
+	if uuid == nil || len(uuid) != 16 {
+		return ""
+	}
+	b := []byte(uuid)
+	return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x",
+		b[:4], b[4:6], b[6:8], b[8:10], b[10:])
+}
+
+// Variant returns the variant encoded in uuid.  It returns Invalid if
+// uuid is invalid.
+func (uuid UUID) Variant() Variant {
+	if len(uuid) != 16 {
+		return Invalid
+	}
+	switch {
+	case (uuid[8] & 0xc0) == 0x80:
+		return RFC4122
+	case (uuid[8] & 0xe0) == 0xc0:
+		return Microsoft
+	case (uuid[8] & 0xe0) == 0xe0:
+		return Future
+	default:
+		return Reserved
+	}
+	panic("unreachable")
+}
+
+// Version returns the verison of uuid.  It returns false if uuid is not
+// valid.
+func (uuid UUID) Version() (Version, bool) {
+	if len(uuid) != 16 {
+		return 0, false
+	}
+	return Version(uuid[6] >> 4), true
+}
+
+func (v Version) String() string {
+	if v > 15 {
+		return fmt.Sprintf("BAD_VERSION_%d", v)
+	}
+	return fmt.Sprintf("VERSION_%d", v)
+}
+
+func (v Variant) String() string {
+	switch v {
+	case RFC4122:
+		return "RFC4122"
+	case Reserved:
+		return "Reserved"
+	case Microsoft:
+		return "Microsoft"
+	case Future:
+		return "Future"
+	case Invalid:
+		return "Invalid"
+	}
+	return fmt.Sprintf("BadVariant%d", int(v))
+}
+
+// SetRand sets the random number generator to r, which implents io.Reader.
+// If r.Read returns an error when the package requests random data then
+// a panic will be issued.
+//
+// Calling SetRand with nil sets the random number generator to the default
+// generator.
+func SetRand(r io.Reader) {
+	if r == nil {
+		rander = rand.Reader
+		return
+	}
+	rander = r
+}

+ 41 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/version1.go

@@ -0,0 +1,41 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+	"encoding/binary"
+)
+
+// NewUUID returns a Version 1 UUID based on the current NodeID and clock
+// sequence, and the current time.  If the NodeID has not been set by SetNodeID
+// or SetNodeInterface then it will be set automatically.  If the NodeID cannot
+// be set NewUUID returns nil.  If clock sequence has not been set by
+// SetClockSequence then it will be set automatically.  If GetTime fails to
+// return the current NewUUID returns nil.
+func NewUUID() UUID {
+	if nodeID == nil {
+		SetNodeInterface("")
+	}
+
+	now, err := GetTime()
+	if err != nil {
+		return nil
+	}
+
+	uuid := make([]byte, 16)
+
+	time_low := uint32(now & 0xffffffff)
+	time_mid := uint16((now >> 32) & 0xffff)
+	time_hi := uint16((now >> 48) & 0x0fff)
+	time_hi |= 0x1000 // Version 1
+
+	binary.BigEndian.PutUint32(uuid[0:], time_low)
+	binary.BigEndian.PutUint16(uuid[4:], time_mid)
+	binary.BigEndian.PutUint16(uuid[6:], time_hi)
+	binary.BigEndian.PutUint16(uuid[8:], clock_seq)
+	copy(uuid[10:], nodeID)
+
+	return uuid
+}

+ 25 - 0
vendor/git.qianqiusoft.com/qianqiusoft/go-uuid/uuid/version4.go

@@ -0,0 +1,25 @@
+// Copyright 2011 Google Inc.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+// Random returns a Random (Version 4) UUID or panics.
+//
+// The strength of the UUIDs is based on the strength of the crypto/rand
+// package.
+//
+// A note about uniqueness derived from from the UUID Wikipedia entry:
+//
+//  Randomly generated UUIDs have 122 random bits.  One's annual risk of being
+//  hit by a meteorite is estimated to be one chance in 17 billion, that
+//  means the probability is about 0.00000000006 (6 × 10−11),
+//  equivalent to the odds of creating a few tens of trillions of UUIDs in a
+//  year and having one duplicate.
+func NewRandom() UUID {
+	uuid := make([]byte, 16)
+	randomBits([]byte(uuid))
+	uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+	uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+	return uuid
+}

+ 4 - 0
vendor/modules.txt

@@ -0,0 +1,4 @@
+# git.qianqiusoft.com/library/glog v0.0.0-20190618112924-96c02a20b1b4
+git.qianqiusoft.com/library/glog
+# git.qianqiusoft.com/qianqiusoft/go-uuid v0.0.0-20160104082328-2bc5388dd0b3
+git.qianqiusoft.com/qianqiusoft/go-uuid/uuid