유틸클레스 잔뜩 추가
This commit is contained in:
parent
e0bebbbb06
commit
251ba74dbe
|
@ -0,0 +1,88 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultByteBufferSize = 4096
|
||||
)
|
||||
|
||||
// ByteBuffer provides byte buffer, which can be used with fasthttp API
|
||||
// in order to minimize memory allocations.
|
||||
//
|
||||
// ByteBuffer may be used with functions appending data to the given []byte
|
||||
// slice. See example code for details.
|
||||
//
|
||||
// Use AcquireByteBuffer for obtaining an empty byte buffer.
|
||||
type ByteBuffer struct {
|
||||
// B is a byte buffer to use in append-like workloads.
|
||||
// See example code for details.
|
||||
B []byte
|
||||
}
|
||||
|
||||
func (b *ByteBuffer) WriteByte(c byte) error {
|
||||
b.B = append(b.B, c)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write implements io.Writer - it appends p to ByteBuffer.B
|
||||
func (b *ByteBuffer) Write(p []byte) (int, error) {
|
||||
b.B = append(b.B, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// WriteString appends s to ByteBuffer.B
|
||||
func (b *ByteBuffer) WriteString(s string) (int, error) {
|
||||
b.B = append(b.B, s...)
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
//(r rune) (n int, err error) {
|
||||
// WriteString appends s to ByteBuffer.B
|
||||
func (b *ByteBuffer) WriteRune(r rune) (n int, err error) {
|
||||
if r < utf8.RuneSelf {
|
||||
b.B = append(b.B, byte(r))
|
||||
return 1, nil
|
||||
}
|
||||
curSize := len(b.B)
|
||||
runCharBuf := b.B[curSize:curSize+utf8.UTFMax]
|
||||
n = utf8.EncodeRune(runCharBuf, r)
|
||||
b.B = b.B[:curSize+n]
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Set sets ByteBuffer.B to p
|
||||
func (b *ByteBuffer) Set(p []byte) {
|
||||
b.B = append(b.B[:0], p...)
|
||||
}
|
||||
|
||||
// SetString sets ByteBuffer.B to s
|
||||
func (b *ByteBuffer) SetString(s string) {
|
||||
b.B = append(b.B[:0], s...)
|
||||
}
|
||||
|
||||
// Reset makes ByteBuffer.B empty.
|
||||
func (b *ByteBuffer) Reset() {
|
||||
b.B = b.B[:0]
|
||||
}
|
||||
|
||||
type ByteBufferPool struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
func (p *ByteBufferPool) Acquire() *ByteBuffer {
|
||||
v := p.pool.Get()
|
||||
if v == nil {
|
||||
return &ByteBuffer{
|
||||
B: make([]byte, 0, defaultByteBufferSize),
|
||||
}
|
||||
}
|
||||
return v.(*ByteBuffer)
|
||||
}
|
||||
|
||||
func (p *ByteBufferPool) Release(b *ByteBuffer) {
|
||||
b.Reset()
|
||||
p.pool.Put(b)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
func NewBytesQueue() (chan<- []byte, <-chan []byte) {
|
||||
send := make(chan []byte, 1)
|
||||
receive := make(chan []byte, 1)
|
||||
go manageBytesQueue(send, receive)
|
||||
return send, receive
|
||||
}
|
||||
|
||||
func manageBytesQueue(send <-chan []byte, receive chan<- []byte) {
|
||||
queue := list.New()
|
||||
for {
|
||||
if front := queue.Front(); front == nil {
|
||||
if send == nil {
|
||||
close(receive)
|
||||
return
|
||||
}
|
||||
value, ok := <-send
|
||||
if !ok {
|
||||
close(receive)
|
||||
return
|
||||
}
|
||||
queue.PushBack(value)
|
||||
} else {
|
||||
select {
|
||||
case receive <- front.Value.([]byte):
|
||||
queue.Remove(front)
|
||||
case value, ok := <-send:
|
||||
if ok {
|
||||
queue.PushBack(value)
|
||||
} else {
|
||||
send = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package buf
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
// 블럭되지 않는 큐체널
|
||||
func NewStringQueue() (chan<- string, <-chan string) {
|
||||
send := make(chan string, 1)
|
||||
receive := make(chan string, 1)
|
||||
go manageStringQueue(send, receive)
|
||||
return send, receive
|
||||
}
|
||||
|
||||
func manageStringQueue(send <-chan string, receive chan<- string) {
|
||||
queue := list.New()
|
||||
for {
|
||||
if front := queue.Front(); front == nil {
|
||||
if send == nil {
|
||||
close(receive)
|
||||
return
|
||||
}
|
||||
value, ok := <-send
|
||||
if !ok {
|
||||
close(receive)
|
||||
return
|
||||
}
|
||||
queue.PushBack(value)
|
||||
} else {
|
||||
select {
|
||||
case receive <- front.Value.(string):
|
||||
queue.Remove(front)
|
||||
case value, ok := <-send:
|
||||
if ok {
|
||||
queue.PushBack(value)
|
||||
} else {
|
||||
send = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"context"
|
||||
"log"
|
||||
)
|
||||
|
||||
// 자식들을 기다리는 context waiter
|
||||
type Handler struct {
|
||||
errorChan chan error
|
||||
ctx context.Context
|
||||
canceler context.CancelFunc
|
||||
waiter *sync.WaitGroup
|
||||
}
|
||||
|
||||
func NewHandler(ctx context.Context) *Handler {
|
||||
ctx, canceler := context.WithCancel(ctx)
|
||||
return &Handler{
|
||||
ctx: ctx,
|
||||
canceler: canceler,
|
||||
waiter: &sync.WaitGroup{},
|
||||
errorChan: make(chan error, 5),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) NotifyError(err error) { h.errorChan <- err }
|
||||
func (h *Handler) Error() <-chan error { return h.errorChan }
|
||||
func (h *Handler) Done() <-chan struct{} { return h.ctx.Done() }
|
||||
func (h *Handler) GracefulWait() {
|
||||
if h.ctx.Err() == nil {
|
||||
h.canceler()
|
||||
}
|
||||
|
||||
h.waiter.Wait()
|
||||
close(h.errorChan)
|
||||
|
||||
for remainError := range h.errorChan {
|
||||
log.Println("remain errors ", remainError)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) IncreaseWait() {
|
||||
h.waiter.Add(1)
|
||||
}
|
||||
func (h *Handler) DecreaseWait() {
|
||||
h.waiter.Done()
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net"
|
||||
"time"
|
||||
"errors"
|
||||
"net/url"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
httpCannotRedirectError = errors.New("this client cannot redirect")
|
||||
|
||||
disableRedirect = func(_ *http.Request, _ []*http.Request) error {
|
||||
return httpCannotRedirectError
|
||||
}
|
||||
limitedRedirect = func(_ *http.Request, via []*http.Request) error {
|
||||
if len(via) >= 10 {
|
||||
return errors.New("stopped after 10 redirects")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
)
|
||||
|
||||
type GracefulClient interface {
|
||||
http.RoundTripper
|
||||
client() *http.Client
|
||||
roundTripper() http.RoundTripper
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
Get(url string) (resp *http.Response, err error)
|
||||
Head(url string) (resp *http.Response, err error)
|
||||
Post(url string, contentType string, body io.Reader) (resp *http.Response, err error)
|
||||
PostForm(url string, data url.Values) (resp *http.Response, err error)
|
||||
}
|
||||
|
||||
|
||||
type wrappedClient struct {
|
||||
http.Client
|
||||
}
|
||||
|
||||
func (cli *wrappedClient) client() (*http.Client) {
|
||||
return &cli.Client
|
||||
}
|
||||
func (cli *wrappedClient) roundTripper() http.RoundTripper {
|
||||
return cli.Transport
|
||||
}
|
||||
|
||||
func (cli *wrappedClient) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return cli.Client.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
func NewClient(
|
||||
keepaliveDuration time.Duration,
|
||||
connectTimeout time.Duration,
|
||||
responseHeaderTimeout time.Duration,
|
||||
idleConnectionTimeout time.Duration,
|
||||
maxIdleConnections int,
|
||||
redirectSupport bool,
|
||||
serverName string,
|
||||
) GracefulClient {
|
||||
|
||||
srvName := []string{serverName}
|
||||
var redirectChecker func(*http.Request, []*http.Request) error
|
||||
if redirectSupport {
|
||||
redirectChecker = limitedRedirect
|
||||
} else {
|
||||
redirectChecker = disableRedirect
|
||||
}
|
||||
|
||||
keepaliveDisabled := keepaliveDuration == 0
|
||||
dialer := &net.Dialer{
|
||||
Timeout: connectTimeout,
|
||||
KeepAlive: keepaliveDuration,
|
||||
DualStack: false,
|
||||
}
|
||||
|
||||
transport := &predefinedHeaderTransport{
|
||||
useragentName: srvName,
|
||||
Transport: http.Transport{
|
||||
Proxy: nil,
|
||||
DialTLS: nil,
|
||||
TLSClientConfig: nil,
|
||||
DisableKeepAlives: keepaliveDisabled,
|
||||
DisableCompression: true,
|
||||
MaxIdleConnsPerHost: maxIdleConnections,
|
||||
DialContext: dialer.DialContext,
|
||||
MaxIdleConns: maxIdleConnections,
|
||||
IdleConnTimeout: idleConnectionTimeout,
|
||||
ResponseHeaderTimeout: responseHeaderTimeout,
|
||||
TLSNextProto: nil,
|
||||
ExpectContinueTimeout: 0,
|
||||
},
|
||||
}
|
||||
|
||||
return &wrappedClient{
|
||||
Client: http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: redirectChecker,
|
||||
Jar: nil,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionHeaderKey = "Connection"
|
||||
connectionUserAgentHeaderKey = "User-Agent"
|
||||
connectionCloseHeader = "close"
|
||||
connectionKeepAliveHeader = "keep-alive"
|
||||
)
|
||||
|
||||
var (
|
||||
connectionCloseHeaderValue = []string{connectionCloseHeader}
|
||||
connectionKeepAliveHeaderValue = []string{connectionKeepAliveHeader}
|
||||
)
|
||||
|
||||
type predefinedHeaderTransport struct {
|
||||
useragentName []string
|
||||
http.Transport
|
||||
}
|
||||
|
||||
func (pht *predefinedHeaderTransport) RoundTrip(req *http.Request) (res *http.Response, err error) {
|
||||
var connectionValue []string
|
||||
if pht.DisableKeepAlives {
|
||||
connectionValue = connectionCloseHeaderValue
|
||||
} else {
|
||||
connectionValue = connectionKeepAliveHeaderValue
|
||||
}
|
||||
req.Header[connectionHeaderKey] = connectionValue
|
||||
req.Header[connectionUserAgentHeaderKey] = pht.useragentName
|
||||
res, err = pht.Transport.RoundTrip(req)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"time"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func SetCookieValue(w http.ResponseWriter, host, key, newCookieValue string) {
|
||||
http.SetCookie(w,
|
||||
&http.Cookie{
|
||||
Name: key,
|
||||
Value: newCookieValue,
|
||||
Path: "/",
|
||||
Domain: host,
|
||||
Expires: time.Now().UTC().AddDate(3, 0, 0),
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
func GetCookieValue(req *http.Request, name string) (cookieValue string) {
|
||||
if cookie, _ := req.Cookie(name); cookie != nil {
|
||||
cookieValue = cookie.Value
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
"sync"
|
||||
myhttp "amuz.es/src/infra/goutils/http"
|
||||
"amuz.es/src/infra/goutils/buf"
|
||||
"amuz.es/src/infra/goutils/misc"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
dateFormat = "02/Jan/2006:15:04:05 -0700"
|
||||
forwardedForIPHeader = "X-Forwarded-For"
|
||||
)
|
||||
|
||||
type bufferedWriter interface {
|
||||
WriteTo(w io.Writer) (n int64, err error)
|
||||
Write(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
type accessLoggerMiddleware struct {
|
||||
serverNameValue []string
|
||||
writer io.Writer
|
||||
waiter *sync.WaitGroup
|
||||
dumpQ bufferedWriter
|
||||
writerFlushChan chan bool
|
||||
}
|
||||
|
||||
func (m *accessLoggerMiddleware) Handle(next http.Handler) http.Handler {
|
||||
accessLoggerBufPool := buf.ByteBufferPool{}
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
m.waiter.Add(1)
|
||||
respWrapper := myhttp.NewWrapResponseWriter(w, r.ProtoMajor)
|
||||
defer func(now time.Time) {
|
||||
dur := time.Since(now)
|
||||
buf := accessLoggerBufPool.Acquire()
|
||||
defer accessLoggerBufPool.Release(buf)
|
||||
buf.WriteString(m.remoteHost(r))
|
||||
buf.WriteString(` - - [`)
|
||||
buf.WriteString(now.Format(dateFormat))
|
||||
buf.WriteString(`] "`)
|
||||
buf.WriteString(r.Method)
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(r.RequestURI)
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(r.Proto)
|
||||
buf.WriteString(`" `)
|
||||
buf.WriteString(misc.FormatInt(respWrapper.Status()))
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(misc.FormatInt(respWrapper.BytesWritten()))
|
||||
buf.WriteString(` "`)
|
||||
buf.WriteString(r.Referer())
|
||||
buf.WriteString(`" "`)
|
||||
buf.WriteString(r.UserAgent())
|
||||
buf.WriteString(`" `)
|
||||
buf.WriteString(misc.FormatInt64(dur.Nanoseconds() / time.Millisecond.Nanoseconds()))
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(r.Host)
|
||||
buf.WriteByte('\n')
|
||||
m.dumpQ.Write(buf.B)
|
||||
m.waiter.Done()
|
||||
}(time.Now().Local())
|
||||
|
||||
respWrapper.Header()["Server"] = m.serverNameValue
|
||||
|
||||
next.ServeHTTP(respWrapper, r)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
func (m *accessLoggerMiddleware) Close() {
|
||||
m.waiter.Wait()
|
||||
m.writerFlushChan <- true
|
||||
<-m.writerFlushChan
|
||||
}
|
||||
|
||||
func (m *accessLoggerMiddleware) lineByLineWriter() {
|
||||
ticker := time.NewTicker(250 * time.Millisecond)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
m.dumpQ.WriteTo(m.writer)
|
||||
close(m.writerFlushChan)
|
||||
}()
|
||||
for {
|
||||
<-ticker.C
|
||||
m.dumpQ.WriteTo(m.writer)
|
||||
select {
|
||||
case <-m.writerFlushChan:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// strip port from addresses with hostname, ipv4 or ipv6
|
||||
func (m *accessLoggerMiddleware) stripPort(address string) string {
|
||||
if h, _, err := net.SplitHostPort(address); err == nil {
|
||||
return h
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
// The remote address of the client. When the 'X-Forwarded-For'
|
||||
// header is set, then it is used instead.
|
||||
func (m *accessLoggerMiddleware) remoteAddr(r *http.Request) string {
|
||||
ff := r.Header.Get(forwardedForIPHeader)
|
||||
if ff != "" {
|
||||
return ff
|
||||
}
|
||||
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
||||
func (m *accessLoggerMiddleware) remoteHost(r *http.Request) string {
|
||||
a := m.remoteAddr(r)
|
||||
h := m.stripPort(a)
|
||||
if h != "" {
|
||||
return h
|
||||
}
|
||||
|
||||
return "-"
|
||||
}
|
||||
|
||||
func AccessLog(serverName string, bufferingWriter bufferedWriter, accessLogWriter io.Writer) (func(next http.Handler) http.Handler, func()) {
|
||||
impl := &accessLoggerMiddleware{}
|
||||
impl.serverNameValue = append(impl.serverNameValue, serverName)
|
||||
impl.writer = accessLogWriter
|
||||
impl.waiter = &sync.WaitGroup{}
|
||||
impl.writerFlushChan = make(chan bool, 1)
|
||||
impl.dumpQ = bufferingWriter
|
||||
go impl.lineByLineWriter()
|
||||
return impl.Handle, impl.Close
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package http
|
||||
|
||||
const (
|
||||
// 200
|
||||
JsonOK ResponseMessage = "{\"message\":\"OK\"}"
|
||||
JsonPartialContent = "{\"message\":\"Partial Content\"}"
|
||||
|
||||
// 300
|
||||
JsonMovedPermanently ResponseMessage = "{\"message\":\"Moved Permanently\"}"
|
||||
JsonFound = "{\"message\":\"Found\"}"
|
||||
JsonSeeOther = "{\"message\":\"See Other\"}"
|
||||
JsonTemporaryRedirect = "{\"message\":\"Temporary Redirect\"}"
|
||||
JsonPermanentRedirect = "{\"message\":\"Permanent Redirect\"}"
|
||||
JsonUnauthorized = "{\"message\":\"Unauthorized\"}"
|
||||
JsonPaymentRequired = "{\"message\":\"Payment Required\"}"
|
||||
|
||||
// 400
|
||||
JsonBadRequest ResponseMessage = "{\"message\":\"Bad Request\"}"
|
||||
JsonForbidden = "{\"message\":\"Forbidden\"}"
|
||||
JsonNotFound = "{\"message\":\"Not Found\"}"
|
||||
JsonMethodNotAllowed = "{\"message\":\"Method Not Allowed\"}"
|
||||
JsonNotAcceptable = "{\"message\":\"Not Acceptable\"}"
|
||||
JsonRequestTimeout = "{\"message\":\"Request Timeout\"}"
|
||||
JsonConflict = "{\"message\":\"Conflict\"}"
|
||||
JsonGone = "{\"message\":\"Gone\"}"
|
||||
JsonPreconditionFailed = "{\"message\":\"Precondition Failed\"}"
|
||||
JsonRequestEntityTooLarge = "{\"message\":\"Request Entity Too Large\"}"
|
||||
JsonRequestURITooLong = "{\"message\":\"Request URI Too Long\"}"
|
||||
JsonUnsupportedMediaType = "{\"message\":\"Unsupported Media Type\"}"
|
||||
JsonUpgradeRequired = "{\"message\":\"Upgrade Required\"}"
|
||||
JsonPreconditionRequired = "{\"message\":\"Precondition Required\"}"
|
||||
JsonTooManyRequests = "{\"message\":\"Too Many Requests\"}"
|
||||
JsonRequestHeaderFieldsTooLarge = "{\"message\":\"Request Header Fields Too Large\"}"
|
||||
JsonUnavailableForLegalReasons = "{\"message\":\"Unavailable For Legal Reasons\"}"
|
||||
|
||||
// 500
|
||||
JsonInternalServerError ResponseMessage = "{\"message\":\"Internal Server Error\"}"
|
||||
JsonNotImplemented = "{\"message\":\"Not Implemented\"}"
|
||||
JsonBadGateway = "{\"message\":\"Bad Gateway\"}"
|
||||
JsonServiceUnavailable = "{\"message\":\"Service Unavailable\"}"
|
||||
JsonGatewayTimeout = "{\"message\":\"Gateway Timeout\"}"
|
||||
JsonHTTPVersionNotSupported = "{\"message\":\"HTTP Version Not Supported\"}"
|
||||
JsonInsufficientStorage = "{\"message\":\"Insufficient Storage\"}"
|
||||
JsonLoopDetected = "{\"message\":\"Loop Detected\"}"
|
||||
)
|
||||
|
||||
var (
|
||||
JsonContentType = []string{"application/json; charset=utf-8"}
|
||||
)
|
||||
|
||||
type ResponseMessage string
|
||||
|
||||
func (rm ResponseMessage) MarshalJSON() ([]byte, error) {
|
||||
return []byte(rm), nil
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
package http
|
||||
|
||||
// The original work was derived from Goji's middleware, source:
|
||||
// https://github.com/zenazn/goji/tree/master/web/middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook
|
||||
// into various parts of the response process.
|
||||
type WrapResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
// Status returns the HTTP status of the request, or 0 if one has not
|
||||
// yet been sent.
|
||||
Status() int
|
||||
// BytesWritten returns the total number of bytes sent to the client.
|
||||
BytesWritten() int
|
||||
// Tee causes the response body to be written to the given io.Writer in
|
||||
// addition to proxying the writes through. Only one io.Writer can be
|
||||
// tee'd to at once: setting a second one will overwrite the first.
|
||||
// Writes will be sent to the proxy before being written to this
|
||||
// io.Writer. It is illegal for the tee'd writer to be modified
|
||||
// concurrently with writes.
|
||||
Tee(io.Writer)
|
||||
// Unwrap returns the original proxied target.
|
||||
Unwrap() http.ResponseWriter
|
||||
}
|
||||
|
||||
// basicWriter wraps a http.ResponseWriter that implements the minimal
|
||||
// http.ResponseWriter interface.
|
||||
type basicWriter struct {
|
||||
http.ResponseWriter
|
||||
wroteHeader bool
|
||||
code int
|
||||
bytes int
|
||||
tee io.Writer
|
||||
}
|
||||
|
||||
func (b *basicWriter) WriteHeader(code int) {
|
||||
if !b.wroteHeader {
|
||||
b.code = code
|
||||
b.wroteHeader = true
|
||||
b.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
}
|
||||
func (b *basicWriter) Write(buf []byte) (int, error) {
|
||||
b.WriteHeader(http.StatusOK)
|
||||
n, err := b.ResponseWriter.Write(buf)
|
||||
if b.tee != nil {
|
||||
_, err2 := b.tee.Write(buf[:n])
|
||||
// Prefer errors generated by the proxied writer.
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
b.bytes += n
|
||||
return n, err
|
||||
}
|
||||
func (b *basicWriter) maybeWriteHeader() {
|
||||
if !b.wroteHeader {
|
||||
b.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
func (b *basicWriter) Status() int {
|
||||
return b.code
|
||||
}
|
||||
func (b *basicWriter) BytesWritten() int {
|
||||
return b.bytes
|
||||
}
|
||||
func (b *basicWriter) Tee(w io.Writer) {
|
||||
b.tee = w
|
||||
}
|
||||
func (b *basicWriter) Unwrap() http.ResponseWriter {
|
||||
return b.ResponseWriter
|
||||
}
|
||||
|
||||
type flushWriter struct {
|
||||
basicWriter
|
||||
}
|
||||
|
||||
func (f *flushWriter) Flush() {
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
|
||||
var _ http.Flusher = &flushWriter{}
|
||||
|
||||
// httpFancyWriter is a HTTP writer that additionally satisfies http.CloseNotifier,
|
||||
// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
|
||||
// of wrapping the http.ResponseWriter that package http gives you, in order to
|
||||
// make the proxied object support the full method set of the proxied object.
|
||||
type httpFancyWriter struct {
|
||||
basicWriter
|
||||
}
|
||||
|
||||
func (f *httpFancyWriter) CloseNotify() <-chan bool {
|
||||
cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
|
||||
return cn.CloseNotify()
|
||||
}
|
||||
func (f *httpFancyWriter) Flush() {
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
hj := f.basicWriter.ResponseWriter.(http.Hijacker)
|
||||
return hj.Hijack()
|
||||
}
|
||||
func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) {
|
||||
if f.basicWriter.tee != nil {
|
||||
n, err := io.Copy(&f.basicWriter, r)
|
||||
f.basicWriter.bytes += int(n)
|
||||
return n, err
|
||||
}
|
||||
rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
|
||||
f.basicWriter.maybeWriteHeader()
|
||||
n, err := rf.ReadFrom(r)
|
||||
f.basicWriter.bytes += int(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
var _ http.CloseNotifier = &httpFancyWriter{}
|
||||
var _ http.Flusher = &httpFancyWriter{}
|
||||
var _ http.Hijacker = &httpFancyWriter{}
|
||||
var _ io.ReaderFrom = &httpFancyWriter{}
|
||||
|
||||
// http2FancyWriter is a HTTP2 writer that additionally satisfies http.CloseNotifier,
|
||||
// http.Flusher, and io.ReaderFrom. It exists for the common case
|
||||
// of wrapping the http.ResponseWriter that package http gives you, in order to
|
||||
// make the proxied object support the full method set of the proxied object.
|
||||
type http2FancyWriter struct {
|
||||
basicWriter
|
||||
}
|
||||
|
||||
func (f *http2FancyWriter) CloseNotify() <-chan bool {
|
||||
cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
|
||||
return cn.CloseNotify()
|
||||
}
|
||||
func (f *http2FancyWriter) Flush() {
|
||||
fl := f.basicWriter.ResponseWriter.(http.Flusher)
|
||||
fl.Flush()
|
||||
}
|
||||
|
||||
func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error {
|
||||
return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts)
|
||||
}
|
||||
|
||||
var _ http.CloseNotifier = &http2FancyWriter{}
|
||||
var _ http.Flusher = &http2FancyWriter{}
|
||||
var _ http.Pusher = &http2FancyWriter{}
|
||||
|
||||
// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to
|
||||
// hook into various parts of the response process.
|
||||
func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter {
|
||||
_, cn := w.(http.CloseNotifier)
|
||||
_, fl := w.(http.Flusher)
|
||||
|
||||
bw := basicWriter{ResponseWriter: w}
|
||||
|
||||
if protoMajor == 2 {
|
||||
_, ps := w.(http.Pusher)
|
||||
if cn && fl && ps {
|
||||
return &http2FancyWriter{bw}
|
||||
}
|
||||
} else {
|
||||
_, hj := w.(http.Hijacker)
|
||||
_, rf := w.(io.ReaderFrom)
|
||||
if cn && fl && hj && rf {
|
||||
return &httpFancyWriter{bw}
|
||||
}
|
||||
}
|
||||
if fl {
|
||||
return &flushWriter{bw}
|
||||
}
|
||||
|
||||
return &bw
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"time"
|
||||
"net"
|
||||
)
|
||||
|
||||
func NewListener(addr *net.TCPAddr) (net.Listener, error) {
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &keepAliveListener{Listener: listener}, nil
|
||||
|
||||
}
|
||||
|
||||
type keepAliveListener struct {
|
||||
// the number of open connections
|
||||
net.Listener
|
||||
}
|
||||
|
||||
func (ln *keepAliveListener) Accept() (c net.Conn, err error) {
|
||||
c, err = ln.Listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if tc, ok := c.(*net.TCPConn); ok {
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io
|
||||
|
||||
import "io"
|
||||
|
||||
type nopReader bool
|
||||
|
||||
func (nopReader) Read(p []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (nopReader) Close() (error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
NopReader nopReader = false
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"context"
|
||||
)
|
||||
|
||||
type wrappedIO struct {
|
||||
stdin io.Reader
|
||||
stdout io.Writer
|
||||
closer context.CancelFunc
|
||||
}
|
||||
|
||||
func (wio *wrappedIO) Read(p []byte) (n int, err error) {
|
||||
return wio.stdin.Read(p)
|
||||
}
|
||||
|
||||
func (wio *wrappedIO) Write(p []byte) (n int, err error) {
|
||||
return wio.stdout.Write(p)
|
||||
}
|
||||
|
||||
func (wio *wrappedIO) Close() (err error) {
|
||||
wio.closer()
|
||||
return io.EOF
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package io
|
||||
|
||||
import (
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
const (
|
||||
linefeed = '\n'
|
||||
LineFeedRemover linefeedRemover = iota
|
||||
)
|
||||
|
||||
type linefeedRemover int
|
||||
|
||||
func (linefeedRemover) Reset() {}
|
||||
func (linefeedRemover) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
cur, start := 0, 0
|
||||
for ; cur < len(src); {
|
||||
if src[cur] == linefeed {
|
||||
if nDst+1 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
return
|
||||
}
|
||||
nSrc += cur - start
|
||||
nDst += copy(dst[nDst:], src[start:cur])
|
||||
cur++
|
||||
start = cur
|
||||
} else {
|
||||
cur++
|
||||
}
|
||||
}
|
||||
if remain := cur - start; remain == 0 {
|
||||
} else if nDst+1 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
} else {
|
||||
nSrc += remain
|
||||
nDst += copy(dst[nDst:], src[start:cur])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Remover struct {
|
||||
Removes []byte
|
||||
}
|
||||
|
||||
func (r *Remover) Reset() {}
|
||||
func (r *Remover) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
|
||||
cur, start := 0, 0
|
||||
|
||||
for ; cur < len(src); {
|
||||
var (
|
||||
curData = src[cur]
|
||||
matched = false
|
||||
)
|
||||
for i := 0; i < len(r.Removes); i++ {
|
||||
if matched = r.Removes[i] == curData; matched {
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
if nDst+1 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
return
|
||||
}
|
||||
nSrc += cur - start
|
||||
nDst += copy(dst[nDst:], src[start:cur])
|
||||
cur++
|
||||
start = cur
|
||||
} else {
|
||||
cur++
|
||||
}
|
||||
}
|
||||
if remain := cur - start; remain == 0 {
|
||||
} else if nDst+1 > len(dst) {
|
||||
err = transform.ErrShortDst
|
||||
} else {
|
||||
nSrc += remain
|
||||
nDst += copy(dst[nDst:], src[start:cur])
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type nopWriter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopWriter) Close() error { return nil }
|
||||
|
||||
// NopCloser returns a ReadCloser with a no-op Close method wrapping
|
||||
// the provided Reader r.
|
||||
func NopCloser(w io.Writer) io.WriteCloser {
|
||||
return nopWriter{w}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package logger
|
||||
|
||||
import "io"
|
||||
|
||||
type WriteSyncer interface {
|
||||
io.Writer
|
||||
Sync() error
|
||||
}
|
||||
|
||||
type RotateSyncer interface {
|
||||
WriteSyncer
|
||||
SetOnClose(func())
|
||||
Rotate() error
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package rotater
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"log"
|
||||
"amuz.es/src/infra/goutils/logger"
|
||||
)
|
||||
|
||||
var loggers logger.RotateSyncerSet
|
||||
|
||||
func NewLogWriter(FileName string, MaxSizeMb, MaxBackup, MaxDay int, logDir string) logger.RotateSyncer {
|
||||
switch FileName {
|
||||
case "Stdout":
|
||||
return newLocked(os.Stdout)
|
||||
case "Stderr":
|
||||
return newLocked(os.Stderr)
|
||||
default:
|
||||
logpath := FileName
|
||||
if logDir != "" {
|
||||
logpath = path.Join(logDir, FileName)
|
||||
}
|
||||
|
||||
log.Println(" Attention!! log writes to ", logpath)
|
||||
|
||||
logWriter := newRotater(
|
||||
logpath,
|
||||
MaxSizeMb, // megabytes
|
||||
MaxBackup,
|
||||
MaxDay, //days
|
||||
)
|
||||
loggers.Store(logWriter)
|
||||
logWriter.SetOnClose(func() { loggers.Delete(logWriter) })
|
||||
return logWriter
|
||||
}
|
||||
}
|
||||
|
||||
func Rotate() {
|
||||
loggers.Range(func(rotater logger.RotateSyncer) {
|
||||
rotater.Sync()
|
||||
rotater.Rotate()
|
||||
})
|
||||
log.Println("rotated")
|
||||
}
|
||||
|
||||
func Close() {
|
||||
loggers.Range(func(rotater logger.RotateSyncer) {
|
||||
rotater.Sync()
|
||||
rotater.Close()
|
||||
})
|
||||
log.Println("end of log")
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package rotater
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"amuz.es/src/infra/goutils/logger"
|
||||
)
|
||||
|
||||
/**
|
||||
logger set
|
||||
*/
|
||||
type RotateSyncerSet struct {
|
||||
storage sync.Map
|
||||
}
|
||||
|
||||
func (s *RotateSyncerSet) Delete(key logger.RotateSyncer) {
|
||||
s.storage.Delete(key)
|
||||
}
|
||||
func (s *RotateSyncerSet) Exist(key logger.RotateSyncer) (ok bool) {
|
||||
_, ok = s.storage.Load(key)
|
||||
return
|
||||
}
|
||||
func (s *RotateSyncerSet) SetNx(key logger.RotateSyncer) (bool) {
|
||||
_, exist := s.storage.LoadOrStore(key, 0)
|
||||
return !exist
|
||||
}
|
||||
func (s *RotateSyncerSet) Range(f func(key logger.RotateSyncer)) {
|
||||
s.storage.Range(s.rangeWrap(f))
|
||||
}
|
||||
func (s *RotateSyncerSet) Store(key logger.RotateSyncer) {
|
||||
s.storage.Store(key, 0)
|
||||
}
|
||||
func (s *RotateSyncerSet) rangeWrap(f func(key logger.RotateSyncer)) func(key, value interface{}) bool {
|
||||
ok := true
|
||||
return func(key, value interface{}) bool {
|
||||
f(key.(logger.RotateSyncer))
|
||||
return ok
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RotateSyncerSet) Len() int {
|
||||
var count uint64
|
||||
s.Range(func(conn logger.RotateSyncer) {
|
||||
atomic.AddUint64(&count, 1)
|
||||
})
|
||||
return int(count)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package rotater
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"amuz.es/src/infra/goutils/logger"
|
||||
)
|
||||
|
||||
type LockedWriteSyncer struct {
|
||||
sync.Mutex
|
||||
ws logger.WriteSyncer
|
||||
}
|
||||
|
||||
// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
|
||||
// particular, *os.Files must be locked before use.
|
||||
func newLocked(ws logger.WriteSyncer) logger.RotateSyncer {
|
||||
if lws, ok := ws.(*LockedWriteSyncer); ok {
|
||||
// no need to layer on another lock
|
||||
return lws
|
||||
}
|
||||
return &LockedWriteSyncer{ws: ws}
|
||||
}
|
||||
|
||||
func (s *LockedWriteSyncer) Write(bs []byte) (int, error) {
|
||||
s.Lock()
|
||||
n, err := s.ws.Write(bs)
|
||||
s.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *LockedWriteSyncer) Sync() error {
|
||||
s.Lock()
|
||||
err := s.ws.Sync()
|
||||
s.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *LockedWriteSyncer) SetOnClose(closeFunc logger.CloseFunc) {}
|
||||
func (r *LockedWriteSyncer) Rotate() (err error) { return }
|
||||
func (r *LockedWriteSyncer) Close() (err error) { return }
|
|
@ -0,0 +1,51 @@
|
|||
package rotater
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"amuz.es/src/infra/goutils/logger"
|
||||
)
|
||||
|
||||
type rotateSyncer struct {
|
||||
setOnceOnclose *sync.Once
|
||||
onClose func()
|
||||
lumberjack.Logger
|
||||
}
|
||||
|
||||
func newRotater(filename string, maxSize, maxBackup, maxDay int) logger.RotateSyncer {
|
||||
return &rotateSyncer{
|
||||
setOnceOnclose: &sync.Once{},
|
||||
Logger: lumberjack.Logger{
|
||||
Filename: filename,
|
||||
MaxSize: maxSize, // megabytes
|
||||
MaxBackups: maxBackup,
|
||||
MaxAge: maxDay, //days
|
||||
LocalTime: false,
|
||||
Compress: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
func (r *rotateSyncer) SetOnClose(closeFunc func()) {
|
||||
r.setOnceOnclose.Do(func() {
|
||||
r.onClose = closeFunc
|
||||
})
|
||||
}
|
||||
|
||||
func (r *rotateSyncer) Rotate() error {
|
||||
return r.Logger.Rotate()
|
||||
}
|
||||
func (r *rotateSyncer) Close() error {
|
||||
defer func() {
|
||||
if r.onClose != nil {
|
||||
r.onClose()
|
||||
}
|
||||
}()
|
||||
return r.Logger.Close()
|
||||
}
|
||||
|
||||
func (r *rotateSyncer) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *rotateSyncer) Write(bs []byte) (int, error) {
|
||||
return s.Logger.Write(bs)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package zap
|
||||
|
||||
import "go.uber.org/zap/zapcore"
|
||||
|
||||
var LogCommonFormat = zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.CapitalLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package zap
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
"amuz.es/src/infra/goutils/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultWriter logger.RotateSyncer
|
||||
defaultErrorOutputOptions []zap.Option
|
||||
nopCloser = func() (err error) { return }
|
||||
)
|
||||
|
||||
func init() {
|
||||
zap.RedirectStdLog(zap.L())
|
||||
}
|
||||
|
||||
func replaceGlobalLogger(newOne *zap.Logger) {
|
||||
zap.ReplaceGlobals(newOne)
|
||||
zap.RedirectStdLog(newOne)
|
||||
}
|
||||
|
||||
func Init(
|
||||
verbose bool,
|
||||
formatter zapcore.Encoder,
|
||||
mainLogName, logFilename, logDir string,
|
||||
maxSizeMb, maxBackup, maxDay int,
|
||||
logLevel zapcore.Level,
|
||||
additionalOptions ...zap.Option,
|
||||
) *zap.SugaredLogger {
|
||||
level := zap.NewAtomicLevelAt(logLevel)
|
||||
defaultWriter = rotater.NewLogWriter(logFilename, maxSizeMb, maxBackup, maxDay, logDir)
|
||||
|
||||
defaultErrorOutputOptions = []zap.Option{zap.ErrorOutput(defaultWriter)}
|
||||
options := defaultErrorOutputOptions
|
||||
if verbose {
|
||||
options = append(options, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.PanicLevel)))
|
||||
}
|
||||
// reset log option slice
|
||||
options = append(options, additionalOptions...)
|
||||
|
||||
log := initLogger(defaultWriter, mainLogName, formatter, level, options...)
|
||||
|
||||
replaceGlobalLogger(log)
|
||||
return log.Sugar()
|
||||
}
|
||||
|
||||
func New(parent *zap.SugaredLogger, moduleName string, options ...zap.Option) *zap.SugaredLogger {
|
||||
var subLogger *zap.Logger
|
||||
if parent == nil {
|
||||
subLogger = zap.L().Named(moduleName)
|
||||
} else {
|
||||
subLogger = parent.Desugar().Named(moduleName)
|
||||
}
|
||||
|
||||
subLogger.WithOptions(options...)
|
||||
|
||||
return subLogger.Sugar()
|
||||
}
|
||||
|
||||
func NewOtherLogger(
|
||||
formatter zapcore.Encoder,
|
||||
moduleName, logFilename, logDir string,
|
||||
maxSizeMb, maxBackup, maxDay int,
|
||||
logLevel zapcore.Level,
|
||||
fields ...zapcore.Field,
|
||||
) (logger *zap.SugaredLogger, closer func() error) {
|
||||
loglevel := zap.NewAtomicLevelAt(logLevel)
|
||||
logWriter := rotater.NewLogWriter(logFilename, maxSizeMb, maxBackup, maxDay, logDir)
|
||||
core := zapcore.NewCore(formatter, logWriter, loglevel)
|
||||
closer = logWriter.Close
|
||||
logger = zap.New(core, defaultErrorOutputOptions...).
|
||||
Named(moduleName).With(fields...).Sugar()
|
||||
return
|
||||
}
|
||||
|
||||
func NewOtherLoggerWithOption(
|
||||
formatter zapcore.Encoder,
|
||||
moduleName, logFilename, logDir string,
|
||||
maxSizeMb, maxBackup, maxDay int,
|
||||
logLevel zapcore.Level,
|
||||
options []zap.Option,
|
||||
fields ...zapcore.Field,
|
||||
) (logger *zap.SugaredLogger, closer func() error) {
|
||||
loglevel := zap.NewAtomicLevelAt(logLevel)
|
||||
logWriter := rotater.NewLogWriter(logFilename, maxSizeMb, maxBackup, maxDay, logDir)
|
||||
core := zapcore.NewCore(formatter, logWriter, loglevel)
|
||||
closer = logWriter.Close
|
||||
options = append(defaultErrorOutputOptions, options...)
|
||||
logger = zap.New(core, options...).
|
||||
Named(moduleName).With(fields...).Sugar()
|
||||
return
|
||||
}
|
||||
func initLogger(
|
||||
writer zapcore.WriteSyncer,
|
||||
moduleName string,
|
||||
formatter zapcore.Encoder,
|
||||
level zap.AtomicLevel,
|
||||
options ...zap.Option,
|
||||
) *zap.Logger {
|
||||
core := zapcore.NewCore(formatter, writer, level)
|
||||
return zap.New(core, options...).Named(moduleName)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package zap
|
||||
|
||||
import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type zapWrappedSyncer struct {
|
||||
zapcore.WriteSyncer
|
||||
}
|
||||
|
||||
func (r *zapWrappedSyncer) SetOnClose(closeFunc func()) {}
|
||||
func (r *zapWrappedSyncer) Rotate() (err error) { return }
|
||||
func (r *zapWrappedSyncer) Close() (err error) { return }
|
||||
func (r *zapWrappedSyncer) Sync() error { return r.WriteSyncer.Sync() }
|
|
@ -0,0 +1,126 @@
|
|||
package misc
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
func mustParseInt(value string, bits int) int64 {
|
||||
if parsed, err := strconv.ParseInt(value, 10, bits); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
func mustParseUint(value string, bits int) uint64 {
|
||||
if parsed, err := strconv.ParseUint(value, 10, bits); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
|
||||
func ParseUint8(value string) (ret *uint8) {
|
||||
|
||||
if parsed, err := strconv.ParseUint(value, 10, 8); err == nil {
|
||||
ret = new(uint8)
|
||||
*ret = uint8(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseUint16(value string) (ret *uint16) {
|
||||
|
||||
if parsed, err := strconv.ParseUint(value, 10, 16); err == nil {
|
||||
ret = new(uint16)
|
||||
*ret = uint16(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseUint32(value string) (ret *uint32) {
|
||||
|
||||
if parsed, err := strconv.ParseUint(value, 10, 32); err == nil {
|
||||
ret = new(uint32)
|
||||
*ret = uint32(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseUint64(value string) (ret *uint64) {
|
||||
|
||||
if parsed, err := strconv.ParseUint(value, 10, 64); err == nil {
|
||||
ret = new(uint64)
|
||||
*ret = uint64(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseUint(value string) (ret *uint) {
|
||||
|
||||
if parsed, err := strconv.ParseUint(value, 10, 0); err == nil {
|
||||
ret = new(uint)
|
||||
*ret = uint(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseInt8(value string) (ret *int8) {
|
||||
|
||||
if parsed, err := strconv.ParseInt(value, 10, 8); err == nil {
|
||||
ret = new(int8)
|
||||
*ret = int8(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseInt16(value string) (ret *int16) {
|
||||
|
||||
if parsed, err := strconv.ParseInt(value, 10, 16); err == nil {
|
||||
ret = new(int16)
|
||||
*ret = int16(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseInt32(value string) (ret *int32) {
|
||||
|
||||
if parsed, err := strconv.ParseInt(value, 10, 32); err == nil {
|
||||
ret = new(int32)
|
||||
*ret = int32(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseInt64(value string) (ret *int64) {
|
||||
|
||||
if parsed, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
ret = new(int64)
|
||||
*ret = int64(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
func ParseInt(value string) (ret *int) {
|
||||
if parsed, err := strconv.ParseInt(value, 10, 0); err == nil {
|
||||
ret = new(int)
|
||||
*ret = int(parsed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseUint8Must(value string) uint8 { return uint8(mustParseUint(value, 8)) }
|
||||
func ParseUint16Must(value string) uint16 { return uint16(mustParseUint(value, 16)) }
|
||||
func ParseUint32Must(value string) uint32 { return uint32(mustParseUint(value, 32)) }
|
||||
func ParseUint64Must(value string) uint64 { return mustParseUint(value, 64) }
|
||||
func ParseUintMust(value string) uint { return uint(mustParseUint(value, 0)) }
|
||||
|
||||
func ParseInt8Must(value string) int8 { return int8(mustParseInt(value, 8)) }
|
||||
func ParseInt16Must(value string) int16 { return int16(mustParseInt(value, 16)) }
|
||||
func ParseInt32Must(value string) int32 { return int32(mustParseInt(value, 32)) }
|
||||
func ParseInt64Must(value string) int64 { return mustParseInt(value, 64) }
|
||||
func ParseIntMust(value string) int { return int(mustParseInt(value, 0)) }
|
||||
|
||||
func FormatUint8(value uint8) string { return strconv.FormatUint(uint64(value), 10) }
|
||||
func FormatUint16(value uint16) string { return strconv.FormatUint(uint64(value), 10) }
|
||||
func FormatUint32(value uint32) string { return strconv.FormatUint(uint64(value), 10) }
|
||||
func FormatUint64(value uint64) string { return strconv.FormatUint(value, 10) }
|
||||
func FormatUint(value uint) string { return strconv.FormatUint(uint64(value), 10) }
|
||||
|
||||
func FormatInt8(value int8) string { return strconv.FormatInt(int64(value), 10) }
|
||||
func FormatInt16(value int16) string { return strconv.FormatInt(int64(value), 10) }
|
||||
func FormatInt32(value int32) string { return strconv.FormatInt(int64(value), 10) }
|
||||
func FormatInt64(value int64) string { return strconv.FormatInt(value, 10) }
|
||||
func FormatInt(value int) string { return strconv.FormatInt(int64(value), 10) }
|
|
@ -0,0 +1,112 @@
|
|||
package misc
|
||||
|
||||
import "bytes"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
func HexDump(by []byte) string {
|
||||
n := len(by)
|
||||
rowcount := 0
|
||||
stop := (n / 16) * 16
|
||||
k := 0
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
for i := 0; i <= stop; i += 16 {
|
||||
k++
|
||||
if i+16 < n {
|
||||
rowcount = 16
|
||||
} else {
|
||||
rowcount = min(k*16, n) % 16
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "%08x ", i)
|
||||
for j := 0; j < rowcount; j++ {
|
||||
if j%8 == 0 {
|
||||
fmt.Fprintf(buf, " %02x ", by[i+j])
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%02x ", by[i+j])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for j := rowcount; j < 16; j++ {
|
||||
if j%8 == 0 {
|
||||
fmt.Fprintf(buf, " ")
|
||||
} else {
|
||||
fmt.Fprintf(buf, " ")
|
||||
}
|
||||
}
|
||||
buf.WriteRune('|')
|
||||
viewString(by[i:(i + rowcount)], buf)
|
||||
buf.WriteRune('|')
|
||||
buf.WriteRune('\n')
|
||||
buf.WriteRune('\r')
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||