log refactor, add socket handler
This commit is contained in:
parent
454a429e10
commit
eb13bf9e9b
|
@ -0,0 +1,179 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileHandler struct {
|
||||||
|
fd *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileHandler(fileName string, flag int) (*FileHandler, error) {
|
||||||
|
f, err := os.OpenFile(fileName, flag, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := new(FileHandler)
|
||||||
|
|
||||||
|
h.fd = f
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *FileHandler) Write(b []byte) (n int, err error) {
|
||||||
|
return h.fd.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *FileHandler) Close() error {
|
||||||
|
return h.fd.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type RotatingFileHandler struct {
|
||||||
|
fd *os.File
|
||||||
|
|
||||||
|
fileName string
|
||||||
|
maxBytes int
|
||||||
|
backupCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRotatingFileHandler(fileName string, maxBytes int, backupCount int) (*RotatingFileHandler, error) {
|
||||||
|
h := new(RotatingFileHandler)
|
||||||
|
|
||||||
|
h.fileName = fileName
|
||||||
|
h.maxBytes = maxBytes
|
||||||
|
h.backupCount = backupCount
|
||||||
|
|
||||||
|
var err error
|
||||||
|
h.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RotatingFileHandler) Write(p []byte) (n int, err error) {
|
||||||
|
h.doRollover()
|
||||||
|
return h.fd.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RotatingFileHandler) Close() error {
|
||||||
|
if h.fd != nil {
|
||||||
|
return h.fd.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *RotatingFileHandler) doRollover() {
|
||||||
|
f, err := h.fd.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.maxBytes <= 0 {
|
||||||
|
return
|
||||||
|
} else if f.Size() < int64(h.maxBytes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.backupCount > 0 {
|
||||||
|
h.fd.Close()
|
||||||
|
|
||||||
|
for i := h.backupCount - 1; i > 0; i-- {
|
||||||
|
sfn := fmt.Sprintf("%s.%d", h.fileName, i)
|
||||||
|
dfn := fmt.Sprintf("%s.%d", h.fileName, i+1)
|
||||||
|
|
||||||
|
os.Rename(sfn, dfn)
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn := fmt.Sprintf("%s.1", h.fileName)
|
||||||
|
os.Rename(h.fileName, dfn)
|
||||||
|
|
||||||
|
h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//refer: http://docs.python.org/2/library/logging.handlers.html
|
||||||
|
//same like python TimedRotatingFileHandler
|
||||||
|
|
||||||
|
type TimeRotatingFileHandler struct {
|
||||||
|
fd *os.File
|
||||||
|
|
||||||
|
baseName string
|
||||||
|
interval int64
|
||||||
|
suffix string
|
||||||
|
rolloverAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
WhenSecond = iota
|
||||||
|
WhenMinute
|
||||||
|
WhenHour
|
||||||
|
WhenDay
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTimeRotatingFileHandler(baseName string, when int8, interval int) (*TimeRotatingFileHandler, error) {
|
||||||
|
h := new(TimeRotatingFileHandler)
|
||||||
|
|
||||||
|
h.baseName = baseName
|
||||||
|
|
||||||
|
switch when {
|
||||||
|
case WhenSecond:
|
||||||
|
h.interval = 1
|
||||||
|
h.suffix = "2006-01-02_15-04-05"
|
||||||
|
case WhenMinute:
|
||||||
|
h.interval = 60
|
||||||
|
h.suffix = "2006-01-02_15-04"
|
||||||
|
case WhenHour:
|
||||||
|
h.interval = 3600
|
||||||
|
h.suffix = "2006-01-02_15"
|
||||||
|
case WhenDay:
|
||||||
|
h.interval = 3600 * 24
|
||||||
|
h.suffix = "2006-01-02"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid when_rotate: %d", when)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.interval = h.interval * int64(interval)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
h.fd, err = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fInfo, _ := h.fd.Stat()
|
||||||
|
h.rolloverAt = fInfo.ModTime().Unix() + h.interval
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TimeRotatingFileHandler) doRollover() {
|
||||||
|
//refer http://hg.python.org/cpython/file/2.7/Lib/logging/handlers.py
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
if h.rolloverAt <= now.Unix() {
|
||||||
|
fName := h.baseName + now.Format(h.suffix)
|
||||||
|
h.fd.Close()
|
||||||
|
e := os.Rename(h.baseName, fName)
|
||||||
|
if e != nil {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.fd, _ = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
|
||||||
|
h.rolloverAt = time.Now().Unix() + h.interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TimeRotatingFileHandler) Write(b []byte) (n int, err error) {
|
||||||
|
h.doRollover()
|
||||||
|
return h.fd.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TimeRotatingFileHandler) Close() error {
|
||||||
|
return h.fd.Close()
|
||||||
|
}
|
175
log/handler.go
175
log/handler.go
|
@ -1,10 +1,7 @@
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
|
@ -46,175 +43,3 @@ func (h *NullHandler) Write(b []byte) (n int, err error) {
|
||||||
func (h *NullHandler) Close() {
|
func (h *NullHandler) Close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileHandler struct {
|
|
||||||
fd *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFileHandler(fileName string, flag int) (*FileHandler, error) {
|
|
||||||
f, err := os.OpenFile(fileName, flag, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := new(FileHandler)
|
|
||||||
|
|
||||||
h.fd = f
|
|
||||||
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *FileHandler) Write(b []byte) (n int, err error) {
|
|
||||||
return h.fd.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *FileHandler) Close() error {
|
|
||||||
return h.fd.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type RotatingFileHandler struct {
|
|
||||||
fd *os.File
|
|
||||||
|
|
||||||
fileName string
|
|
||||||
maxBytes int
|
|
||||||
backupCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRotatingFileHandler(fileName string, maxBytes int, backupCount int) (*RotatingFileHandler, error) {
|
|
||||||
h := new(RotatingFileHandler)
|
|
||||||
|
|
||||||
h.fileName = fileName
|
|
||||||
h.maxBytes = maxBytes
|
|
||||||
h.backupCount = backupCount
|
|
||||||
|
|
||||||
var err error
|
|
||||||
h.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RotatingFileHandler) Write(p []byte) (n int, err error) {
|
|
||||||
h.doRollover()
|
|
||||||
return h.fd.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RotatingFileHandler) Close() error {
|
|
||||||
if h.fd != nil {
|
|
||||||
return h.fd.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *RotatingFileHandler) doRollover() {
|
|
||||||
f, err := h.fd.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.maxBytes <= 0 {
|
|
||||||
return
|
|
||||||
} else if f.Size() < int64(h.maxBytes) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.backupCount > 0 {
|
|
||||||
h.fd.Close()
|
|
||||||
|
|
||||||
for i := h.backupCount - 1; i > 0; i-- {
|
|
||||||
sfn := fmt.Sprintf("%s.%d", h.fileName, i)
|
|
||||||
dfn := fmt.Sprintf("%s.%d", h.fileName, i+1)
|
|
||||||
|
|
||||||
os.Rename(sfn, dfn)
|
|
||||||
}
|
|
||||||
|
|
||||||
dfn := fmt.Sprintf("%s.1", h.fileName)
|
|
||||||
os.Rename(h.fileName, dfn)
|
|
||||||
|
|
||||||
h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//refer: http://docs.python.org/2/library/logging.handlers.html
|
|
||||||
//same like python TimedRotatingFileHandler
|
|
||||||
|
|
||||||
type TimeRotatingFileHandler struct {
|
|
||||||
fd *os.File
|
|
||||||
|
|
||||||
baseName string
|
|
||||||
interval int64
|
|
||||||
suffix string
|
|
||||||
rolloverAt int64
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
WhenSecond = iota
|
|
||||||
WhenMinute
|
|
||||||
WhenHour
|
|
||||||
WhenDay
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewTimeRotatingFileHandler(baseName string, when int8, interval int) (*TimeRotatingFileHandler, error) {
|
|
||||||
h := new(TimeRotatingFileHandler)
|
|
||||||
|
|
||||||
h.baseName = baseName
|
|
||||||
|
|
||||||
switch when {
|
|
||||||
case WhenSecond:
|
|
||||||
h.interval = 1
|
|
||||||
h.suffix = "2006-01-02_15-04-05"
|
|
||||||
case WhenMinute:
|
|
||||||
h.interval = 60
|
|
||||||
h.suffix = "2006-01-02_15-04"
|
|
||||||
case WhenHour:
|
|
||||||
h.interval = 3600
|
|
||||||
h.suffix = "2006-01-02_15"
|
|
||||||
case WhenDay:
|
|
||||||
h.interval = 3600 * 24
|
|
||||||
h.suffix = "2006-01-02"
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid when_rotate: %d", when)
|
|
||||||
}
|
|
||||||
|
|
||||||
h.interval = h.interval * int64(interval)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
h.fd, err = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fInfo, _ := h.fd.Stat()
|
|
||||||
h.rolloverAt = fInfo.ModTime().Unix() + h.interval
|
|
||||||
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *TimeRotatingFileHandler) doRollover() {
|
|
||||||
//refer http://hg.python.org/cpython/file/2.7/Lib/logging/handlers.py
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
if h.rolloverAt <= now.Unix() {
|
|
||||||
fName := h.baseName + now.Format(h.suffix)
|
|
||||||
h.fd.Close()
|
|
||||||
e := os.Rename(h.baseName, fName)
|
|
||||||
if e != nil {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
h.fd, _ = os.OpenFile(h.baseName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
|
|
||||||
h.rolloverAt = time.Now().Unix() + h.interval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *TimeRotatingFileHandler) Write(b []byte) (n int, err error) {
|
|
||||||
h.doRollover()
|
|
||||||
return h.fd.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *TimeRotatingFileHandler) Close() error {
|
|
||||||
return h.fd.Close()
|
|
||||||
}
|
|
||||||
|
|
|
@ -125,7 +125,10 @@ func (l *Logger) Output(callDepth int, level int, format string, v ...interface{
|
||||||
s := fmt.Sprintf(format, v...)
|
s := fmt.Sprintf(format, v...)
|
||||||
|
|
||||||
buf = append(buf, s...)
|
buf = append(buf, s...)
|
||||||
buf = append(buf, "\n"...)
|
|
||||||
|
if s[len(s)-1] != '\n' {
|
||||||
|
buf = append(buf, "\n"...)
|
||||||
|
}
|
||||||
|
|
||||||
l.msg <- buf
|
l.msg <- buf
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"github.com/siddontang/golib/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logFile = flag.String("logfile", "./logd.log", "file to log")
|
||||||
|
var net = flag.String("net", "tcp", "server listen protocol, like tcp, udp or unix")
|
||||||
|
var addr = flag.String("addr", "127.0.0.1:11183", "server listen address")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
s, err := log.NewServer(*logFile, *net, *addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Run()
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
//a log server for handling SocketHandler send log
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
closed bool
|
||||||
|
listener net.Listener
|
||||||
|
fd *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(fileName string, protocol string, addr string) (*Server, error) {
|
||||||
|
s := new(Server)
|
||||||
|
|
||||||
|
s.closed = false
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
dir := path.Dir(fileName)
|
||||||
|
os.Mkdir(dir, 0777)
|
||||||
|
|
||||||
|
s.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.listener, err = net.Listen(protocol, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Close() error {
|
||||||
|
if s.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.closed = true
|
||||||
|
|
||||||
|
s.fd.Close()
|
||||||
|
|
||||||
|
s.listener.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Run() {
|
||||||
|
for {
|
||||||
|
conn, err := s.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.onRead(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) onRead(c net.Conn) {
|
||||||
|
br := bufio.NewReaderSize(c, 1024)
|
||||||
|
|
||||||
|
var bufLen uint32
|
||||||
|
|
||||||
|
for {
|
||||||
|
if err := binary.Read(br, binary.BigEndian, &bufLen); err != nil {
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, bufLen, bufLen+1)
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(br, buf); err != nil && err != io.ErrUnexpectedEOF {
|
||||||
|
c.Close()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if buf[len(buf)-1] != '\n' {
|
||||||
|
buf = append(buf, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
s.fd.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSocket(t *testing.T) {
|
||||||
|
fileName := "./test_server.log"
|
||||||
|
|
||||||
|
os.Remove(fileName)
|
||||||
|
|
||||||
|
s, err := NewServer(fileName, "tcp", "127.0.0.1:11183")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
go s.Run()
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
var h *SocketHandler
|
||||||
|
h, err = NewSocketHandler("tcp", "127.0.0.1:11183")
|
||||||
|
|
||||||
|
_, err = h.Write([]byte("hello world"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
s.Close()
|
||||||
|
|
||||||
|
var f *os.File
|
||||||
|
f, err = os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
var n int
|
||||||
|
n, err = f.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[0:n]
|
||||||
|
|
||||||
|
if string(buf) != "hello world\n" {
|
||||||
|
t.Fatal(string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(fileName)
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SocketHandler struct {
|
||||||
|
c net.Conn
|
||||||
|
protocol string
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSocketHandler(protocol string, addr string) (*SocketHandler, error) {
|
||||||
|
s := new(SocketHandler)
|
||||||
|
|
||||||
|
s.protocol = protocol
|
||||||
|
s.addr = addr
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SocketHandler) Write(p []byte) (n int, err error) {
|
||||||
|
if err = h.connect(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, len(p)+4)
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(buf, uint32(len(p)))
|
||||||
|
|
||||||
|
copy(buf[4:], p)
|
||||||
|
|
||||||
|
n, err = h.c.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
h.c.Close()
|
||||||
|
h.c = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SocketHandler) Close() error {
|
||||||
|
if h.c != nil {
|
||||||
|
h.c.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SocketHandler) connect() error {
|
||||||
|
if h.c != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
h.c, err = net.DialTimeout(h.protocol, h.addr, 20*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue