mirror of
https://github.com/nabbar/golib.git
synced 2025-09-26 20:01:15 +08:00
Package Profiling
- new package to use/consume CPU / MEM pprof root package - create a file on same location as runable binary to store profile Package HTTPClient - add message function called on each Dial/DialContext call function - function message can be nil Package Server - add generic function for recover message / catching - implement this recovring function into runner StartStop & Ticker Package Logger: - implement generic recovering function into hook - fix bug if instance is an invalid instance of fields or entry
This commit is contained in:
@@ -146,7 +146,7 @@ func (o *componentHttpClient) _runCli() error {
|
||||
|
||||
if cfg, err = o._getConfig(); err != nil {
|
||||
return prt.Error(err)
|
||||
} else if dns = cfg.New(o.x.GetContext(), o.getRootCA); dns == nil {
|
||||
} else if dns = cfg.New(o.x.GetContext(), o.getRootCA, o.getMessage()); dns == nil {
|
||||
return prt.Error(fmt.Errorf("cannot create DNS Mapper"))
|
||||
}
|
||||
|
||||
|
@@ -43,15 +43,17 @@ type ComponentHTTPClient interface {
|
||||
Config() htcdns.Config
|
||||
SetDefault()
|
||||
SetAsDefaultHTTPClient(flag bool)
|
||||
SetFuncMessage(f htcdns.FuncMessage)
|
||||
}
|
||||
|
||||
func New(ctx libctx.FuncContext, defCARoot libtls.FctRootCA, isDeftHTTPClient bool) ComponentHTTPClient {
|
||||
func New(ctx libctx.FuncContext, defCARoot libtls.FctRootCA, isDeftHTTPClient bool, msg htcdns.FuncMessage) ComponentHTTPClient {
|
||||
c := &componentHttpClient{
|
||||
x: libctx.NewConfig[uint8](ctx),
|
||||
c: new(atomic.Value),
|
||||
d: new(atomic.Value),
|
||||
f: new(atomic.Value),
|
||||
s: new(atomic.Bool),
|
||||
m: new(atomic.Value),
|
||||
}
|
||||
|
||||
if defCARoot == nil {
|
||||
@@ -60,8 +62,13 @@ func New(ctx libctx.FuncContext, defCARoot libtls.FctRootCA, isDeftHTTPClient bo
|
||||
}
|
||||
}
|
||||
|
||||
if msg == nil {
|
||||
msg = func(msg string) {}
|
||||
}
|
||||
|
||||
c.f.Store(defCARoot)
|
||||
c.s.Store(isDeftHTTPClient)
|
||||
c.m.Store(msg)
|
||||
|
||||
return c
|
||||
}
|
||||
@@ -70,8 +77,8 @@ func Register(cfg libcfg.Config, key string, cpt ComponentHTTPClient) {
|
||||
cfg.ComponentSet(key, cpt)
|
||||
}
|
||||
|
||||
func RegisterNew(ctx libctx.FuncContext, cfg libcfg.Config, key string, defCARoot libtls.FctRootCA, isDeftHTTPClient bool) {
|
||||
cfg.ComponentSet(key, New(ctx, defCARoot, isDeftHTTPClient))
|
||||
func RegisterNew(ctx libctx.FuncContext, cfg libcfg.Config, key string, defCARoot libtls.FctRootCA, isDeftHTTPClient bool, msg htcdns.FuncMessage) {
|
||||
cfg.ComponentSet(key, New(ctx, defCARoot, isDeftHTTPClient, msg))
|
||||
}
|
||||
|
||||
func Load(getCpt cfgtps.FuncCptGet, key string) ComponentHTTPClient {
|
||||
|
@@ -41,6 +41,7 @@ type componentHttpClient struct {
|
||||
d *atomic.Value // htcdns.DNSMapper
|
||||
f *atomic.Value // FuncDefaultCARoot
|
||||
s *atomic.Bool // is Default at start / update
|
||||
m *atomic.Value // htcdns.FctMessage
|
||||
}
|
||||
|
||||
func (o *componentHttpClient) getRootCA() []string {
|
||||
@@ -55,6 +56,22 @@ func (o *componentHttpClient) getRootCA() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *componentHttpClient) getMessage() htcdns.FuncMessage {
|
||||
if i := o.m.Load(); i == nil {
|
||||
return nil
|
||||
} else if v, k := i.(htcdns.FuncMessage); !k {
|
||||
return nil
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (o *componentHttpClient) SetFuncMessage(f htcdns.FuncMessage) {
|
||||
if f != nil {
|
||||
o.m.Store(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *componentHttpClient) getDNSMapper() htcdns.DNSMapper {
|
||||
if i := o.d.Load(); i == nil {
|
||||
return nil
|
||||
|
@@ -73,7 +73,7 @@ func initDNSMapper() htcdns.DNSMapper {
|
||||
TimeoutIdleConn: libdur.ParseDuration(30 * time.Second),
|
||||
TimeoutResponseHeader: 0,
|
||||
},
|
||||
}, nil)
|
||||
}, nil, nil)
|
||||
}
|
||||
|
||||
func DefaultDNSMapper() htcdns.DNSMapper {
|
||||
|
@@ -121,6 +121,6 @@ func (o Config) Validate() liberr.Error {
|
||||
return e
|
||||
}
|
||||
|
||||
func (o Config) New(ctx context.Context, fct libtls.FctRootCA) DNSMapper {
|
||||
return New(ctx, &o, fct)
|
||||
func (o Config) New(ctx context.Context, fct libtls.FctRootCA, msg FuncMessage) DNSMapper {
|
||||
return New(ctx, &o, fct, msg)
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ package dns_mapper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
libdur "github.com/nabbar/golib/duration"
|
||||
@@ -88,7 +89,9 @@ func init() {
|
||||
addDns("*.*.test.example.com", numIdx("*"), addIdx("127.0.0."), numIdx("8"))
|
||||
idx++
|
||||
|
||||
dns = htcdns.New(ctx, &opt, nil)
|
||||
dns = htcdns.New(ctx, &opt, nil, func(msg string) {
|
||||
_, _ = fmt.Fprintln(os.Stdout, msg)
|
||||
})
|
||||
}
|
||||
|
||||
func addIdx(src string) string {
|
||||
|
@@ -38,6 +38,8 @@ import (
|
||||
libdur "github.com/nabbar/golib/duration"
|
||||
)
|
||||
|
||||
type FuncMessage func(msg string)
|
||||
|
||||
type DNSMapper interface {
|
||||
Add(from, to string)
|
||||
Get(from string) string
|
||||
@@ -58,7 +60,7 @@ type DNSMapper interface {
|
||||
TimeCleaner(ctx context.Context, dur time.Duration)
|
||||
}
|
||||
|
||||
func New(ctx context.Context, cfg *Config, fct libtls.FctRootCA) DNSMapper {
|
||||
func New(ctx context.Context, cfg *Config, fct libtls.FctRootCA, msg FuncMessage) DNSMapper {
|
||||
if cfg == nil {
|
||||
cfg = &Config{
|
||||
DNSMapper: make(map[string]string),
|
||||
@@ -76,12 +78,17 @@ func New(ctx context.Context, cfg *Config, fct libtls.FctRootCA) DNSMapper {
|
||||
}
|
||||
}
|
||||
|
||||
if msg == nil {
|
||||
msg = func(msg string) {}
|
||||
}
|
||||
|
||||
d := &dmp{
|
||||
d: new(sync.Map),
|
||||
z: new(sync.Map),
|
||||
c: new(atomic.Value),
|
||||
t: new(atomic.Value),
|
||||
f: fct,
|
||||
i: msg,
|
||||
}
|
||||
|
||||
for edp, adr := range cfg.DNSMapper {
|
||||
|
@@ -41,6 +41,7 @@ type dmp struct {
|
||||
c *atomic.Value // *Config
|
||||
t *atomic.Value // *http transport
|
||||
f libtls.FctRootCA
|
||||
i func(msg string)
|
||||
}
|
||||
|
||||
func (o *dmp) config() *Config {
|
||||
@@ -99,3 +100,9 @@ func (o *dmp) TimeCleaner(ctx context.Context, dur time.Duration) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (o *dmp) Message(msg string) {
|
||||
if o.i != nil {
|
||||
o.i(msg)
|
||||
}
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ package dns_mapper
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -60,6 +61,7 @@ func (o *dmp) DialContext(ctx context.Context, network, address string) (net.Con
|
||||
if dst, e = o.SearchWithCache(address); e != nil {
|
||||
return nil, e
|
||||
} else {
|
||||
o.Message(fmt.Sprintf("Dialing '%s %s' => '%s %s'", network, address, network, dst))
|
||||
o.CacheSet(address, dst)
|
||||
return d.DialContext(ctx, network, dst)
|
||||
}
|
||||
|
@@ -26,8 +26,10 @@
|
||||
package httpcli_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
libdur "github.com/nabbar/golib/duration"
|
||||
@@ -59,7 +61,9 @@ var _ = Describe("HttpCli", func() {
|
||||
Transport: htcdns.TransportConfig{},
|
||||
}
|
||||
|
||||
dns = htcdns.New(ctx, &opt, nil)
|
||||
dns = htcdns.New(ctx, &opt, nil, func(msg string) {
|
||||
_, _ = fmt.Fprintln(os.Stdout, msg)
|
||||
})
|
||||
|
||||
cli = dns.DefaultClient()
|
||||
Expect(cli).ToNot(BeNil())
|
||||
|
@@ -33,23 +33,45 @@ import (
|
||||
|
||||
// FieldAdd allow to add one couple key/val as type string/interface into the custom field of the entry.
|
||||
func (e *entry) FieldAdd(key string, val interface{}) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
} else if e.Fields == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Fields.Add(key, val)
|
||||
return e
|
||||
}
|
||||
|
||||
// FieldMerge allow to merge a Field pointer into the custom field of the entry.
|
||||
func (e *entry) FieldMerge(fields logfld.Fields) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
} else if e.Fields == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Fields.Merge(fields)
|
||||
return e
|
||||
}
|
||||
|
||||
// FieldSet allow to change the custom field of the entry with the given Fields in parameter.
|
||||
func (e *entry) FieldSet(fields logfld.Fields) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Fields = fields
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) FieldClean(keys ...string) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
} else if e.Fields == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
e.Fields.Delete(k)
|
||||
}
|
||||
|
@@ -76,42 +76,72 @@ type entry struct {
|
||||
}
|
||||
|
||||
func (e *entry) SetEntryContext(etime time.Time, stack uint64, caller, file string, line uint64, msg string) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Time = etime
|
||||
e.Stack = stack
|
||||
e.Caller = caller
|
||||
e.File = file
|
||||
e.Line = line
|
||||
e.Message = msg
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) SetMessageOnly(flag bool) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.clean = flag
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) SetLevel(lvl loglvl.Level) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Level = lvl
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) SetLogger(fct func() *logrus.Logger) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.log = fct
|
||||
return e
|
||||
}
|
||||
|
||||
// SetGinContext allow to register a gin context pointer to register the errors of the current entry intro gin Context Error Slice.
|
||||
func (e *entry) SetGinContext(ctx *ginsdk.Context) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.gin = ctx
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) DataSet(data interface{}) Entry {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.Data = data
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *entry) Check(lvlNoErr loglvl.Level) bool {
|
||||
if e == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var found = false
|
||||
if len(e.Error) > 0 {
|
||||
for _, er := range e.Error {
|
||||
@@ -135,6 +165,8 @@ func (e *entry) Check(lvlNoErr loglvl.Level) bool {
|
||||
func (e *entry) Log() {
|
||||
if e == nil {
|
||||
return
|
||||
} else if e.log == nil {
|
||||
return
|
||||
} else if e.Fields == nil {
|
||||
return
|
||||
} else if e.Fields.Err() != nil {
|
||||
|
@@ -40,12 +40,26 @@ type fldModel struct {
|
||||
}
|
||||
|
||||
func (o *fldModel) Add(key string, val interface{}) Fields {
|
||||
if o == nil {
|
||||
return nil
|
||||
} else if o.Config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.Store(key, val)
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *fldModel) Logrus() logrus.Fields {
|
||||
var res = make(logrus.Fields, 0)
|
||||
|
||||
if o == nil {
|
||||
return res
|
||||
} else if o.Config == nil {
|
||||
return res
|
||||
}
|
||||
|
||||
o.Walk(func(key string, val interface{}) bool {
|
||||
res[key] = val
|
||||
return true
|
||||
@@ -54,10 +68,17 @@ func (o *fldModel) Logrus() logrus.Fields {
|
||||
}
|
||||
|
||||
func (o *fldModel) Map(fct func(key string, val interface{}) interface{}) Fields {
|
||||
if o == nil {
|
||||
return nil
|
||||
} else if o.Config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.Walk(func(key string, val interface{}) bool {
|
||||
o.Store(key, fct(key, val))
|
||||
return true
|
||||
})
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
@@ -37,6 +37,7 @@ import (
|
||||
"time"
|
||||
|
||||
libiot "github.com/nabbar/golib/ioutils"
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
const sizeBuffer = 32 * 1024
|
||||
@@ -81,9 +82,7 @@ func (o *hkf) writeBuffer(buf *bytes.Buffer) error {
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on writeBuffer function in golib/logger/hookfile/system.\nfor log file '%s'\n%v\n", p, rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/logger/hookfile/system", recover())
|
||||
if h != nil {
|
||||
_ = h.Close()
|
||||
}
|
||||
@@ -109,9 +108,7 @@ func (o *hkf) writeBuffer(buf *bytes.Buffer) error {
|
||||
|
||||
func (o *hkf) freeBuffer(buf *bytes.Buffer, size int) *bytes.Buffer {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on freeBuffer function in golib/logger/hookfile/system.\nfor log file '%s'\n%v\n", o.getFilepath(), rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/logger/hookfile/system", recover(), fmt.Sprintf("log file: %s", o.getFilepath()))
|
||||
}()
|
||||
|
||||
var a = o.newBuffer(o.getBufferSize())
|
||||
|
@@ -33,6 +33,8 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
func (o *hks) Run(ctx context.Context) {
|
||||
@@ -43,9 +45,7 @@ func (o *hks) Run(ctx context.Context) {
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on run function in golib/logger/hooksyslog/system\n%v\n", rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/logger/hooksyslog/system", recover())
|
||||
if s != nil {
|
||||
w.Wait()
|
||||
_ = s.Close()
|
||||
|
153
pprof/tools.go
Normal file
153
pprof/tools.go
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Nicolas JUHEL
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package pprof
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
srvtck "github.com/nabbar/golib/server/runner/ticker"
|
||||
)
|
||||
|
||||
var (
|
||||
c *os.File
|
||||
m string
|
||||
ctx, cnl = context.WithCancel(context.Background())
|
||||
s = srvtck.New(5*time.Minute, ProfilingMemRun)
|
||||
)
|
||||
|
||||
func StartProfiling() {
|
||||
ProfilingCPUStart()
|
||||
ProfilingMemStart()
|
||||
}
|
||||
|
||||
func StopProfiling() {
|
||||
ProfilingMemDefer()
|
||||
ProfilingCPUDefer()
|
||||
}
|
||||
|
||||
func getPath(basename string) (*os.File, error) {
|
||||
var (
|
||||
h *os.File
|
||||
p string
|
||||
e error
|
||||
)
|
||||
|
||||
p, e = os.Executable()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
p = filepath.Join(filepath.Dir(p), basename)
|
||||
|
||||
if _, e = os.Stat(p); e != nil && !errors.Is(e, os.ErrNotExist) {
|
||||
return nil, e
|
||||
} else if e != nil {
|
||||
h, e = os.Create(p)
|
||||
} else {
|
||||
h, e = os.Open(p)
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
if e = h.Truncate(0); e != nil {
|
||||
_ = h.Close()
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func ProfilingCPUStart() {
|
||||
var e error
|
||||
if c, e = getPath("cpu.prof"); e != nil {
|
||||
panic(e)
|
||||
} else if e = pprof.StartCPUProfile(c); e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(os.Stdout, "Starting pprof for CPU to file '%s'", c.Name())
|
||||
}
|
||||
|
||||
func ProfilingCPUDefer() {
|
||||
_, _ = fmt.Fprintf(os.Stdout, "Stopping pprof for CPU to file '%s'", c.Name())
|
||||
pprof.StopCPUProfile()
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
func ProfilingMemStart() {
|
||||
if h, e := getPath("mem.prof"); e != nil {
|
||||
panic(e)
|
||||
} else {
|
||||
m = h.Name()
|
||||
}
|
||||
|
||||
if e := s.Start(ctx); e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func ProfilingMemRun(ctx context.Context, tck *time.Ticker) error {
|
||||
if ctx.Err() != nil {
|
||||
return nil
|
||||
} else if len(m) < 1 {
|
||||
return nil
|
||||
} else if h, e := os.OpenFile(m, os.O_RDWR|os.O_EXCL|os.O_SYNC, 0644); e != nil {
|
||||
return e
|
||||
} else {
|
||||
defer func() {
|
||||
_ = h.Close()
|
||||
}()
|
||||
|
||||
runtime.GC()
|
||||
|
||||
if e = pprof.WriteHeapProfile(h); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ProfilingMemDefer() {
|
||||
if cnl != nil {
|
||||
cnl()
|
||||
}
|
||||
|
||||
x, l := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer l()
|
||||
|
||||
_ = s.Stop(x)
|
||||
}
|
@@ -30,7 +30,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -100,9 +99,7 @@ func (o *run) Stop(ctx context.Context) error {
|
||||
o.t.Store(time.Time{})
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on Stop function in gollib/server/startStop/model.\n%v\n", rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/server/startstop", recover())
|
||||
t.Stop()
|
||||
}()
|
||||
|
||||
@@ -143,9 +140,7 @@ func (o *run) Start(ctx context.Context) error {
|
||||
|
||||
o.chanInit()
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on Start function in gollib/server/startStop/model.\n%v\n", rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/server/startstop", recover())
|
||||
_ = o.Stop(ctx)
|
||||
}()
|
||||
|
||||
|
@@ -29,10 +29,10 @@ package ticker
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
libsrv "github.com/nabbar/golib/server"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -116,9 +116,7 @@ func (o *run) Start(ctx context.Context) error {
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic thread on Start function in gollib/server/ticker/model.\n%v\n", rec)
|
||||
}
|
||||
libsrv.RecoveryCaller("golib/server/ticker", recover())
|
||||
if n != nil {
|
||||
n()
|
||||
}
|
||||
@@ -137,11 +135,7 @@ func (o *run) Start(ctx context.Context) error {
|
||||
select {
|
||||
case <-tck.C:
|
||||
f := func(ctx context.Context, tck *time.Ticker) error {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "recovering panic while calling function.\n%v\n", rec)
|
||||
}
|
||||
}()
|
||||
defer libsrv.RecoveryCaller("golib/server/ticker", recover())
|
||||
return o.getFunction()(ctx, tck)
|
||||
}
|
||||
if e := f(x, tck); e != nil {
|
||||
|
@@ -27,7 +27,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -74,3 +78,47 @@ func RunTick(ctx context.Context, tick, max time.Duration, chk FunCheck, run Fun
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RecoveryCaller(proc string, rec any, data ...any) {
|
||||
if rec == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
buf = bytes.NewBuffer(make([]byte, 0))
|
||||
|
||||
// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need.
|
||||
pCnt = make([]uintptr, 10, 255)
|
||||
nCnt = runtime.Callers(1, pCnt)
|
||||
)
|
||||
|
||||
buf.WriteString(fmt.Sprintf("Receoring process '%s': %v\n", proc, rec))
|
||||
for _, d := range data {
|
||||
buf.WriteString(fmt.Sprintf("%v\n", d))
|
||||
}
|
||||
|
||||
if nCnt > 0 {
|
||||
var (
|
||||
frames = runtime.CallersFrames(pCnt[:nCnt])
|
||||
more = true
|
||||
lCnt = 0
|
||||
)
|
||||
|
||||
for more && lCnt < 10 {
|
||||
var frame runtime.Frame
|
||||
frame, more = frames.Next()
|
||||
|
||||
if len(frame.File) > 0 {
|
||||
buf.WriteString(fmt.Sprintf(" trace #%d => Line: %d - File: %s\n", lCnt, frame.Line, frame.File))
|
||||
lCnt++
|
||||
} else if len(frame.Function) > 0 {
|
||||
buf.WriteString(fmt.Sprintf(" trace #%d => Line: %d - Func: %s\n", lCnt, frame.Line, frame.Function))
|
||||
lCnt++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
_, _ = fmt.Fprint(os.Stderr, buf.Bytes())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user