Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]refactor:Soft connect logger to cwgo-pkg #55

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
5 changes: 1 addition & 4 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ jobs:
- name: Check License Header
uses: apache/skywalking-eyes/header@501a28d2fb4a9b962661987e50cf0219631b32ff

- name: typos-action
uses: crate-ci/typos@master

resolve-modules:
name: resolve module
runs-on: ubuntu-latest
Expand All @@ -35,7 +32,7 @@ jobs:
steps:
- uses: actions/setup-go@v4
with:
go-version: "1.21.1"
go-version: "1.21"
cache: false
- uses: actions/checkout@v4
- name: Lint
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.21.1
go-version: 1.21

- uses: actions/cache@v3
with:
Expand Down
149 changes: 3 additions & 146 deletions accesslog/accesslog.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,158 +43,15 @@ package accesslog

import (
"context"
"fmt"
"os"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"

"github.com/cloudwego-contrib/cwgo-pkg/log/accesslog"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/common/bytebufferpool"
)

var defaultFormat = " %s | %3d | %7v | %-7s | %-s "

func New(opts ...Option) app.HandlerFunc {
return new(context.Background(), opts...)
return accesslog.New(opts...)
}

func NewWithContext(ctx context.Context, opts ...Option) app.HandlerFunc {
return new(ctx, opts...)
}

func new(ctx context.Context, opts ...Option) app.HandlerFunc {
cfg := newOptions(opts...)
// Check if format contains latency
cfg.enableLatency = strings.Contains(cfg.format, "${latency}")

// Create correct time format
var timestamp atomic.Value
timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.timeFormat))

// Update date/time every 500 milliseconds in a separate go routine
if strings.Contains(cfg.format, "${time}") {
go func() {
for {
select {
case <-time.After(cfg.timeInterval):
case <-ctx.Done():
return
}
timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.timeFormat))
}
}()
}

// Set PID once and add tag
pid := strconv.Itoa(os.Getpid())

dataPool := sync.Pool{
New: func() interface{} {
return &Data{}
},
}

// instead of analyzing the template inside(handler) each time, this is done once before
// and we create several slices of the same length with the functions to be executed and fixed parts.
tmplChain, logFunChain, err := buildLogFuncChain(cfg, Tags)
if err != nil {
panic(err)
}

return func(ctx context.Context, c *app.RequestContext) {
var start, stop time.Time

// Logger data
data := dataPool.Get().(*Data) //nolint:forcetypeassert,errcheck // We store nothing else in the pool
// no need for a reset, as long as we always override everything
data.Pid = pid
data.Timestamp = timestamp
// put data back in the pool
defer dataPool.Put(data)

// Set latency start time
if cfg.enableLatency {
data.Start = time.Now()
}

c.Next(ctx)

if !cfg.logConditionFunc(ctx, c) {
return
}

if cfg.enableLatency {
data.Stop = time.Now()
}

// Get new buffer
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)

if cfg.format == defaultTagFormat {
// format log to buffer
_, _ = buf.WriteString(fmt.Sprintf(defaultFormat,
timestamp,
c.Response.StatusCode(),
stop.Sub(start),
c.Method(),
c.Path(),
))

cfg.logFunc(ctx, buf.String())
return
}

// Loop over template parts execute dynamic parts and add fixed parts to the buffer
for i, logFunc := range logFunChain {
if logFunc == nil {
_, _ = buf.Write(tmplChain[i]) //nolint:errcheck // This will never fail
} else if tmplChain[i] == nil {
_, err = logFunc(buf, c, data, "")
} else {
_, err = logFunc(buf, c, data, unsafeString(tmplChain[i]))
}
if err != nil {
break
}
}

// Also write errors to the buffer
if err != nil {
_, _ = buf.WriteString(err.Error())
}

cfg.logFunc(ctx, buf.String())
}
}

func appendInt(output Buffer, v int) (int, error) {
old := output.Len()
output.Set(appendUint(output.Bytes(), v))
return output.Len() - old, nil
}

func appendUint(dst []byte, n int) []byte {
if n < 0 {
panic("BUG: int must be positive")
}

var b [20]byte
buf := b[:]
i := len(buf)
var q int
for n >= 10 {
i--
q = n / 10
buf[i] = '0' + byte(n-q*10)
n = q
}
i--
buf[i] = '0' + byte(n)

dst = append(dst, buf[i:]...)
return dst
return accesslog.NewWithContext(ctx, opts...)
}
30 changes: 28 additions & 2 deletions accesslog/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
module github.com/hertz-contrib/logger/accesslog

go 1.16
go 1.21

require github.com/cloudwego/hertz v0.7.2
require (
github.com/cloudwego-contrib/cwgo-pkg/log/accesslog v0.0.0-00010101000000-000000000000
github.com/cloudwego/hertz v0.7.2
)

require (
github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect
github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 // indirect
github.com/bytedance/sonic v1.8.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudwego/netpoll v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/henrylee2cn/ameda v1.4.10 // indirect
github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/nyaruka/phonenumbers v1.0.55 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
google.golang.org/protobuf v1.27.1 // indirect
)

replace github.com/cloudwego-contrib/cwgo-pkg/log/accesslog => github.com/smx-Morgan/cwgo-pkg/log/accesslog v0.0.0-20241019002536-84cf43046703
2 changes: 2 additions & 0 deletions accesslog/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smx-Morgan/cwgo-pkg/log/accesslog v0.0.0-20241019002536-84cf43046703 h1:OGsj2gJNg/Xi+zIWAQT5ywPjoOiCby4hRAkTtttfAso=
github.com/smx-Morgan/cwgo-pkg/log/accesslog v0.0.0-20241019002536-84cf43046703/go.mod h1:k8o+J4eSyMBm9UFYdULTyN2RKlKh5/I8VN8NDHujnq0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
85 changes: 10 additions & 75 deletions accesslog/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,106 +45,41 @@ import (
"context"
"time"

"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego-contrib/cwgo-pkg/log/accesslog"

"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/cloudwego/hertz/pkg/app"
)

type (
logConditionFunc func(ctx context.Context, c *app.RequestContext) bool

// options defines the config for middleware.
options struct {
// format defines the logging tags
//
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
format string

// timeFormat defines timestamp format https://programming.guide/go/format-parse-string-time-date-example.html
//
// Optional. Default: 15:04:05
timeFormat string

// timeInterval is the delay before the timestamp is updated
//
// Optional. Default: 500 * time.Millisecond
timeInterval time.Duration

// logFunc custom define log function
//
// Optional. Default: hlog.CtxInfof
logFunc func(ctx context.Context, format string, v ...interface{})

// timeZoneLocation can be specified time zone
//
// Optional. Default: time.Local
timeZoneLocation *time.Location
enableLatency bool
logConditionFunc logConditionFunc
}

Option func(o *options)
Option = accesslog.Option
)

var defaultTagFormat = "[${time}] ${status} - ${latency} ${method} ${path}"

func newOptions(opts ...Option) *options {
cfg := &options{
format: defaultTagFormat,
timeFormat: "15:04:05",
timeZoneLocation: time.Local,
timeInterval: 500 * time.Millisecond,
logFunc: hlog.CtxInfof,
logConditionFunc: func(ctx context.Context, c *app.RequestContext) bool {
return true
},
}

for _, opt := range opts {
opt(cfg)
}

return cfg
}

// WithFormat set log format
func WithFormat(s string) Option {
return func(o *options) {
o.format = s
}
return accesslog.WithFormat(s)
}

// WithTimeFormat set log time format
func WithTimeFormat(s string) Option {
return func(o *options) {
o.timeFormat = s
}
return accesslog.WithTimeFormat(s)
}

// WithTimeInterval set timestamp refresh interval
func WithTimeInterval(t time.Duration) Option {
return func(o *options) {
o.timeInterval = t
}
return accesslog.WithTimeInterval(t)
}

// WithAccessLogFunc set print log function
func WithAccessLogFunc(f func(ctx context.Context, format string, v ...interface{})) Option {
return func(o *options) {
o.logFunc = f
}
return accesslog.WithAccessLogFunc(f)
}

// WithTimeZoneLocation set timestamp zone
func WithTimeZoneLocation(loc *time.Location) Option {
return func(o *options) {
o.timeZoneLocation = loc
}
return accesslog.WithTimeZoneLocation(loc)
}

// WithLogConditionFunc set logConditionFunc
func WithLogConditionFunc(f logConditionFunc) Option {
return func(o *options) {
o.logConditionFunc = f
}
func WithLogConditionFunc(f func(ctx context.Context, c *app.RequestContext) bool) Option {
return accesslog.WithLogConditionFunc(f)
}
Loading
Loading