Compare commits
5 Commits
558c6f4d7c
...
v3.11.37
| Author | SHA1 | Date | |
|---|---|---|---|
| fcc4faff8a | |||
| 5df8f83f45 | |||
|
|
27fa6e9173 | ||
| bd55a35dc3 | |||
| 653bd386cc |
@@ -26,24 +26,24 @@ jobs:
|
|||||||
|
|
||||||
- name: test coverage
|
- name: test coverage
|
||||||
run: |
|
run: |
|
||||||
go test -v -cover ./... -coverprofile coverage.out -coverpkg ./...
|
go test -v -cover ./... -covermode=count -coverprofile coverage.out -coverpkg ./...
|
||||||
go tool cover -func coverage.out -o coverage.out
|
go tool cover -func coverage.out -o coverage.out
|
||||||
|
|
||||||
- name: coverage badge
|
- name: coverage badge
|
||||||
uses: tj-actions/coverage-badge-go@v1
|
uses: tj-actions/coverage-badge-go@v2
|
||||||
with:
|
with:
|
||||||
green: 80
|
green: 80
|
||||||
filename: coverage.out
|
filename: coverage.out
|
||||||
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||||
id: auto-commit-action
|
name: autocommit
|
||||||
with:
|
with:
|
||||||
commit_message: Apply Code Coverage Badge
|
commit_message: Apply Code Coverage Badge
|
||||||
skip_fetch: true
|
skip_fetch: true
|
||||||
skip_checkout: true
|
skip_checkout: true
|
||||||
file_pattern: ./README.md
|
file_pattern: ./README.md
|
||||||
|
|
||||||
- name: Push Changes
|
- name: push
|
||||||
if: steps.auto-commit-action.outputs.changes_detected == 'true'
|
if: steps.auto-commit-action.outputs.changes_detected == 'true'
|
||||||
uses: ad-m/github-push-action@master
|
uses: ad-m/github-push-action@master
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Micro
|
# Micro
|
||||||

|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
|
[](https://pkg.go.dev/go.unistack.org/micro/v3?tab=overview)
|
||||||
|
[](https://git.unistack.org/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Av3+event%3Apush)
|
||||||
|
[](https://goreportcard.com/report/go.unistack.org/micro/v3)
|
||||||
|

|
||||||
|
|
||||||
Micro is a standard library for microservices.
|
Micro is a standard library for microservices.
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
"go.unistack.org/micro/v3/util/buffer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// always first to have proper check
|
// always first to have proper check
|
||||||
@@ -30,11 +31,30 @@ func TestStacktrace(t *testing.T) {
|
|||||||
|
|
||||||
l.Error(ctx, "msg1", errors.New("err"))
|
l.Error(ctx, "msg1", errors.New("err"))
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`slog_test.go:31`)) {
|
if !bytes.Contains(buf.Bytes(), []byte(`slog_test.go:32`)) {
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelayedBuffer(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
dbuf := buffer.NewDelayedBuffer(100, 100*time.Millisecond, buf)
|
||||||
|
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(dbuf),
|
||||||
|
WithHandlerFunc(slog.NewTextHandler),
|
||||||
|
logger.WithAddStacktrace(true),
|
||||||
|
)
|
||||||
|
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Error(ctx, "msg1", errors.New("err"))
|
||||||
|
time.Sleep(120 * time.Millisecond)
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`key1=val1`)) {
|
||||||
|
t.Fatalf("logger delayed buffer not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTime(t *testing.T) {
|
func TestTime(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package buf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ io.Closer = &Buffer{}
|
|
||||||
|
|
||||||
// Buffer bytes.Buffer wrapper to satisfie io.Closer interface
|
|
||||||
type Buffer struct {
|
|
||||||
*bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close reset buffer contents
|
|
||||||
func (b *Buffer) Close() error {
|
|
||||||
b.Buffer.Reset()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates new buffer that satisfies Closer interface
|
|
||||||
func New(b *bytes.Buffer) *Buffer {
|
|
||||||
if b == nil {
|
|
||||||
b = bytes.NewBuffer(nil)
|
|
||||||
}
|
|
||||||
return &Buffer{b}
|
|
||||||
}
|
|
||||||
85
util/buffer/buffer.go
Normal file
85
util/buffer/buffer.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package buffer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.WriteCloser = (*DelayedBuffer)(nil)
|
||||||
|
|
||||||
|
// DelayedBuffer is the buffer that holds items until either the buffer filled or a specified time limit is reached
|
||||||
|
type DelayedBuffer struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
maxWait time.Duration
|
||||||
|
flushTime time.Time
|
||||||
|
buffer chan []byte
|
||||||
|
ticker *time.Ticker
|
||||||
|
w io.Writer
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDelayedBuffer(size int, maxWait time.Duration, w io.Writer) *DelayedBuffer {
|
||||||
|
b := &DelayedBuffer{
|
||||||
|
buffer: make(chan []byte, size),
|
||||||
|
ticker: time.NewTicker(maxWait),
|
||||||
|
w: w,
|
||||||
|
flushTime: time.Now(),
|
||||||
|
maxWait: maxWait,
|
||||||
|
}
|
||||||
|
b.loop()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DelayedBuffer) loop() {
|
||||||
|
go func() {
|
||||||
|
for range b.ticker.C {
|
||||||
|
b.mu.Lock()
|
||||||
|
if time.Since(b.flushTime) > b.maxWait {
|
||||||
|
b.flush()
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DelayedBuffer) flush() {
|
||||||
|
bufLen := len(b.buffer)
|
||||||
|
if bufLen > 0 {
|
||||||
|
tmp := make([][]byte, bufLen)
|
||||||
|
for i := 0; i < bufLen; i++ {
|
||||||
|
tmp[i] = <-b.buffer
|
||||||
|
}
|
||||||
|
for _, t := range tmp {
|
||||||
|
_, b.err = b.w.Write(t)
|
||||||
|
}
|
||||||
|
b.flushTime = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DelayedBuffer) Put(items ...[]byte) {
|
||||||
|
b.mu.Lock()
|
||||||
|
for _, item := range items {
|
||||||
|
select {
|
||||||
|
case b.buffer <- item:
|
||||||
|
default:
|
||||||
|
b.flush()
|
||||||
|
b.buffer <- item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DelayedBuffer) Close() error {
|
||||||
|
b.mu.Lock()
|
||||||
|
b.flush()
|
||||||
|
close(b.buffer)
|
||||||
|
b.ticker.Stop()
|
||||||
|
b.mu.Unlock()
|
||||||
|
return b.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *DelayedBuffer) Write(data []byte) (int, error) {
|
||||||
|
b.Put(data)
|
||||||
|
return len(data), b.err
|
||||||
|
}
|
||||||
22
util/buffer/buffer_test.go
Normal file
22
util/buffer/buffer_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package buffer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTimedBuffer(t *testing.T) {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
b := NewDelayedBuffer(100, 300*time.Millisecond, buf)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
_, _ = b.Write([]byte(`test`))
|
||||||
|
}
|
||||||
|
if buf.Len() != 0 {
|
||||||
|
t.Fatal("delayed write not worked")
|
||||||
|
}
|
||||||
|
time.Sleep(400 * time.Millisecond)
|
||||||
|
if buf.Len() == 0 {
|
||||||
|
t.Fatal("delayed write not worked")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,11 +35,11 @@ func TestUnmarshalYAML(t *testing.T) {
|
|||||||
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = yaml.Unmarshal([]byte(`{"ttl":"1y"}`), v)
|
err = yaml.Unmarshal([]byte(`{"ttl":"1d"}`), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if *(v.TTL) != 31622400000000000 {
|
} else if *(v.TTL) != 86400000000000 {
|
||||||
t.Fatalf("invalid duration %v != 31622400000000000", v.TTL)
|
t.Fatalf("invalid duration %v != 86400000000000", *v.TTL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +68,11 @@ func TestUnmarshalJSON(t *testing.T) {
|
|||||||
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`{"ttl":"1y"}`), v)
|
err = json.Unmarshal([]byte(`{"ttl":"1d"}`), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v.TTL != 31622400000000000 {
|
} else if v.TTL != 86400000000000 {
|
||||||
t.Fatalf("invalid duration %v != 31622400000000000", v.TTL)
|
t.Fatalf("invalid duration %v != 86400000000000", v.TTL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +87,11 @@ func TestParseDuration(t *testing.T) {
|
|||||||
if td.String() != "340h0m0s" {
|
if td.String() != "340h0m0s" {
|
||||||
t.Fatalf("ParseDuration 14d != 340h0m0s : %s", td.String())
|
t.Fatalf("ParseDuration 14d != 340h0m0s : %s", td.String())
|
||||||
}
|
}
|
||||||
td, err = ParseDuration("1y")
|
td, err = ParseDuration("1d")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ParseDuration error: %v", err)
|
t.Fatalf("ParseDuration error: %v", err)
|
||||||
}
|
}
|
||||||
if td.String() != "8784h0m0s" {
|
if td.String() != "24h0m0s" {
|
||||||
t.Fatalf("ParseDuration 1y != 8784h0m0s : %s", td.String())
|
t.Fatalf("ParseDuration 1d != 24h0m0s : %s", td.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user