Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | b4433c42f0 | ||
| 44ec3b663b | |||
| 4bb73514e9 | 
| @@ -1,5 +1,5 @@ | |||||||
| # Micro | # Micro | ||||||
|  |  | ||||||
| [](https://opensource.org/licenses/Apache-2.0) | [](https://opensource.org/licenses/Apache-2.0) | ||||||
| [](https://pkg.go.dev/go.unistack.org/micro/v3?tab=overview) | [](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://git.unistack.org/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Av3+event%3Apush) | ||||||
|   | |||||||
| @@ -4,18 +4,20 @@ package logger | |||||||
| type Level int8 | type Level int8 | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	// TraceLevel level usually used to find bugs, very verbose | 	// TraceLevel usually used to find bugs, very verbose | ||||||
| 	TraceLevel Level = iota - 2 | 	TraceLevel Level = iota - 2 | ||||||
| 	// DebugLevel level used only when enabled debugging | 	// DebugLevel used only when enabled debugging | ||||||
| 	DebugLevel | 	DebugLevel | ||||||
| 	// InfoLevel level used for general info about what's going on inside the application | 	// InfoLevel used for general info about what's going on inside the application | ||||||
| 	InfoLevel | 	InfoLevel | ||||||
| 	// WarnLevel level used for non-critical entries | 	// WarnLevel used for non-critical entries | ||||||
| 	WarnLevel | 	WarnLevel | ||||||
| 	// ErrorLevel level used for errors that should definitely be noted | 	// ErrorLevel used for errors that should definitely be noted | ||||||
| 	ErrorLevel | 	ErrorLevel | ||||||
| 	// FatalLevel level used for critical errors and then calls `os.Exit(1)` | 	// FatalLevel used for critical errors and then calls `os.Exit(1)` | ||||||
| 	FatalLevel | 	FatalLevel | ||||||
|  | 	// NoneLevel used to disable logging | ||||||
|  | 	NoneLevel | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // String returns logger level string representation | // String returns logger level string representation | ||||||
| @@ -33,6 +35,8 @@ func (l Level) String() string { | |||||||
| 		return "error" | 		return "error" | ||||||
| 	case FatalLevel: | 	case FatalLevel: | ||||||
| 		return "fatal" | 		return "fatal" | ||||||
|  | 	case NoneLevel: | ||||||
|  | 		return "none" | ||||||
| 	} | 	} | ||||||
| 	return "info" | 	return "info" | ||||||
| } | } | ||||||
| @@ -58,6 +62,8 @@ func ParseLevel(lvl string) Level { | |||||||
| 		return ErrorLevel | 		return ErrorLevel | ||||||
| 	case FatalLevel.String(): | 	case FatalLevel.String(): | ||||||
| 		return FatalLevel | 		return FatalLevel | ||||||
|  | 	case NoneLevel.String(): | ||||||
|  | 		return NoneLevel | ||||||
| 	} | 	} | ||||||
| 	return InfoLevel | 	return InfoLevel | ||||||
| } | } | ||||||
|   | |||||||
| @@ -52,6 +52,12 @@ type Options struct { | |||||||
| 	AddStacktrace bool | 	AddStacktrace bool | ||||||
| 	// DedupKeys deduplicate keys in log output | 	// DedupKeys deduplicate keys in log output | ||||||
| 	DedupKeys bool | 	DedupKeys bool | ||||||
|  | 	// FatalFinalizers runs in order in [logger.Fatal] method | ||||||
|  | 	FatalFinalizers []func(context.Context) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var DefaultFatalFinalizer = func(ctx context.Context) { | ||||||
|  | 	os.Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions creates new options struct | // NewOptions creates new options struct | ||||||
| @@ -65,6 +71,7 @@ func NewOptions(opts ...Option) Options { | |||||||
| 		AddSource:        true, | 		AddSource:        true, | ||||||
| 		TimeFunc:         time.Now, | 		TimeFunc:         time.Now, | ||||||
| 		Meter:            meter.DefaultMeter, | 		Meter:            meter.DefaultMeter, | ||||||
|  | 		FatalFinalizers:  []func(context.Context){DefaultFatalFinalizer}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	WithMicroKeys()(&options) | 	WithMicroKeys()(&options) | ||||||
| @@ -76,6 +83,13 @@ func NewOptions(opts ...Option) Options { | |||||||
| 	return options | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithFatalFinalizers set logger.Fatal finalizers | ||||||
|  | func WithFatalFinalizers(fncs ...func(context.Context)) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.FatalFinalizers = fncs | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithContextAttrFuncs appends default funcs for the context attrs filler | // WithContextAttrFuncs appends default funcs for the context attrs filler | ||||||
| func WithContextAttrFuncs(fncs ...ContextAttrFunc) Option { | func WithContextAttrFuncs(fncs ...ContextAttrFunc) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
|   | |||||||
| @@ -4,14 +4,12 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| 	"go.unistack.org/micro/v3/semconv" | 	"go.unistack.org/micro/v3/semconv" | ||||||
| @@ -34,6 +32,7 @@ var ( | |||||||
| 	warnValue  = slog.StringValue("warn") | 	warnValue  = slog.StringValue("warn") | ||||||
| 	errorValue = slog.StringValue("error") | 	errorValue = slog.StringValue("error") | ||||||
| 	fatalValue = slog.StringValue("fatal") | 	fatalValue = slog.StringValue("fatal") | ||||||
|  | 	noneValue  = slog.StringValue("none") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type wrapper struct { | type wrapper struct { | ||||||
| @@ -85,6 +84,8 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | |||||||
| 			a.Value = errorValue | 			a.Value = errorValue | ||||||
| 		case lvl >= logger.FatalLevel: | 		case lvl >= logger.FatalLevel: | ||||||
| 			a.Value = fatalValue | 			a.Value = fatalValue | ||||||
|  | 		case lvl >= logger.NoneLevel: | ||||||
|  | 			a.Value = noneValue | ||||||
| 		default: | 		default: | ||||||
| 			a.Value = infoValue | 			a.Value = infoValue | ||||||
| 		} | 		} | ||||||
| @@ -228,11 +229,12 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{} | |||||||
|  |  | ||||||
| func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| 	s.printLog(ctx, logger.FatalLevel, msg, attrs...) | 	s.printLog(ctx, logger.FatalLevel, msg, attrs...) | ||||||
|  | 	for _, fn := range s.opts.FatalFinalizers { | ||||||
|  | 		fn(ctx) | ||||||
|  | 	} | ||||||
| 	if closer, ok := s.opts.Out.(io.Closer); ok { | 	if closer, ok := s.opts.Out.(io.Closer); ok { | ||||||
| 		closer.Close() | 		closer.Close() | ||||||
| 	} | 	} | ||||||
| 	time.Sleep(1 * time.Second) |  | ||||||
| 	os.Exit(1) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -316,6 +318,8 @@ func loggerToSlogLevel(level logger.Level) slog.Level { | |||||||
| 		return slog.LevelDebug - 1 | 		return slog.LevelDebug - 1 | ||||||
| 	case logger.FatalLevel: | 	case logger.FatalLevel: | ||||||
| 		return slog.LevelError + 1 | 		return slog.LevelError + 1 | ||||||
|  | 	case logger.NoneLevel: | ||||||
|  | 		return slog.LevelError + 2 | ||||||
| 	default: | 	default: | ||||||
| 		return slog.LevelInfo | 		return slog.LevelInfo | ||||||
| 	} | 	} | ||||||
| @@ -333,6 +337,8 @@ func slogToLoggerLevel(level slog.Level) logger.Level { | |||||||
| 		return logger.TraceLevel | 		return logger.TraceLevel | ||||||
| 	case slog.LevelError + 1: | 	case slog.LevelError + 1: | ||||||
| 		return logger.FatalLevel | 		return logger.FatalLevel | ||||||
|  | 	case slog.LevelError + 2: | ||||||
|  | 		return logger.NoneLevel | ||||||
| 	default: | 	default: | ||||||
| 		return logger.InfoLevel | 		return logger.InfoLevel | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -36,6 +36,24 @@ func TestStacktrace(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestNoneLevel(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	l := NewLogger(logger.WithLevel(logger.NoneLevel), logger.WithOutput(buf), | ||||||
|  | 		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")) | ||||||
|  |  | ||||||
|  | 	if buf.Len() != 0 { | ||||||
|  | 		t.Fatalf("logger none level not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestDelayedBuffer(t *testing.T) { | func TestDelayedBuffer(t *testing.T) { | ||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	buf := bytes.NewBuffer(nil) | 	buf := bytes.NewBuffer(nil) | ||||||
| @@ -405,15 +423,16 @@ func TestLogger(t *testing.T) { | |||||||
| func Test_WithContextAttrFunc(t *testing.T) { | func Test_WithContextAttrFunc(t *testing.T) { | ||||||
| 	loggerContextAttrFuncs := []logger.ContextAttrFunc{ | 	loggerContextAttrFuncs := []logger.ContextAttrFunc{ | ||||||
| 		func(ctx context.Context) []interface{} { | 		func(ctx context.Context) []interface{} { | ||||||
| 			md, ok := metadata.FromIncomingContext(ctx) | 			md, ok := metadata.FromOutgoingContext(ctx) | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				return nil | 				return nil | ||||||
| 			} | 			} | ||||||
| 			attrs := make([]interface{}, 0, 10) | 			attrs := make([]interface{}, 0, 10) | ||||||
| 			for k, v := range md { | 			for k, v := range md { | ||||||
| 				switch k { | 				key := strings.ToLower(k) | ||||||
| 				case "X-Request-Id", "Phone", "External-Id", "Source-Service", "X-App-Install-Id", "Client-Id", "Client-Ip": | 				switch key { | ||||||
| 					attrs = append(attrs, strings.ToLower(k), v) | 				case "x-request-id", "phone", "external-Id", "source-service", "x-app-install-id", "client-id", "client-ip": | ||||||
|  | 					attrs = append(attrs, key, v) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			return attrs | 			return attrs | ||||||
| @@ -423,7 +442,7 @@ func Test_WithContextAttrFunc(t *testing.T) { | |||||||
| 	logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs, loggerContextAttrFuncs...) | 	logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs, loggerContextAttrFuncs...) | ||||||
|  |  | ||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	ctx = metadata.AppendIncomingContext(ctx, "X-Request-Id", uuid.New().String(), | 	ctx = metadata.AppendOutgoingContext(ctx, "X-Request-Id", uuid.New().String(), | ||||||
| 		"Source-Service", "Test-System") | 		"Source-Service", "Test-System") | ||||||
|  |  | ||||||
| 	buf := bytes.NewBuffer(nil) | 	buf := bytes.NewBuffer(nil) | ||||||
| @@ -436,17 +455,39 @@ func Test_WithContextAttrFunc(t *testing.T) { | |||||||
| 	if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test message"`))) { | 	if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test message"`))) { | ||||||
| 		t.Fatalf("logger info, buf %s", buf.Bytes()) | 		t.Fatalf("logger info, buf %s", buf.Bytes()) | ||||||
| 	} | 	} | ||||||
| 	if !(bytes.Contains(buf.Bytes(), []byte(`"x-request-id":"`))) { | 	if !(bytes.Contains(buf.Bytes(), []byte(`"x-request-id":`))) { | ||||||
| 		t.Fatalf("logger info, buf %s", buf.Bytes()) | 		t.Fatalf("logger info, buf %s", buf.Bytes()) | ||||||
| 	} | 	} | ||||||
| 	if !(bytes.Contains(buf.Bytes(), []byte(`"source-service":"Test-System"`))) { | 	if !(bytes.Contains(buf.Bytes(), []byte(`"source-service":"Test-System"`))) { | ||||||
| 		t.Fatalf("logger info, buf %s", buf.Bytes()) | 		t.Fatalf("logger info, buf %s", buf.Bytes()) | ||||||
| 	} | 	} | ||||||
| 	buf.Reset() | 	buf.Reset() | ||||||
| 	imd, _ := metadata.FromIncomingContext(ctx) | 	omd, _ := metadata.FromOutgoingContext(ctx) | ||||||
| 	l.Info(ctx, "test message1") | 	l.Info(ctx, "test message1") | ||||||
| 	imd.Set("Source-Service", "Test-System2") | 	omd.Set("Source-Service", "Test-System2") | ||||||
| 	l.Info(ctx, "test message2") | 	l.Info(ctx, "test message2") | ||||||
|  |  | ||||||
| 	// t.Logf("xxx %s", buf.Bytes()) | 	// t.Logf("xxx %s", buf.Bytes()) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestFatalFinalizers(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	l := NewLogger( | ||||||
|  | 		logger.WithLevel(logger.TraceLevel), | ||||||
|  | 		logger.WithOutput(buf), | ||||||
|  | 	) | ||||||
|  | 	if err := l.Init( | ||||||
|  | 		logger.WithFatalFinalizers(func(ctx context.Context) { | ||||||
|  | 			l.Info(ctx, "fatal finalizer") | ||||||
|  | 		})); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	l.Fatal(ctx, "info_msg1") | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte("fatal finalizer")) { | ||||||
|  | 		t.Fatalf("logger dont have fatal message, buf %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte("info_msg1")) { | ||||||
|  | 		t.Fatalf("logger dont have info_msg1 message, buf %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ package meter | |||||||
| import ( | import ( | ||||||
| 	"io" | 	"io" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strconv" |  | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -49,9 +49,11 @@ type Meter interface { | |||||||
| 	Set(opts ...Option) Meter | 	Set(opts ...Option) Meter | ||||||
| 	// Histogram get or create histogram | 	// Histogram get or create histogram | ||||||
| 	Histogram(name string, labels ...string) Histogram | 	Histogram(name string, labels ...string) Histogram | ||||||
|  | 	// HistogramExt get or create histogram with specified quantiles | ||||||
|  | 	HistogramExt(name string, quantiles []float64, labels ...string) Histogram | ||||||
| 	// Summary get or create summary | 	// Summary get or create summary | ||||||
| 	Summary(name string, labels ...string) Summary | 	Summary(name string, labels ...string) Summary | ||||||
| 	// SummaryExt get or create summary with spcified quantiles and window time | 	// SummaryExt get or create summary with specified quantiles and window time | ||||||
| 	SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary | 	SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary | ||||||
| 	// Write writes metrics to io.Writer | 	// Write writes metrics to io.Writer | ||||||
| 	Write(w io.Writer, opts ...Option) error | 	Write(w io.Writer, opts ...Option) error | ||||||
| @@ -59,6 +61,8 @@ type Meter interface { | |||||||
| 	Options() Options | 	Options() Options | ||||||
| 	// String return meter type | 	// String return meter type | ||||||
| 	String() string | 	String() string | ||||||
|  | 	// Unregister metric name and drop all data | ||||||
|  | 	Unregister(name string, labels ...string) bool | ||||||
| } | } | ||||||
|  |  | ||||||
| // Counter is a counter | // Counter is a counter | ||||||
| @@ -80,7 +84,11 @@ type FloatCounter interface { | |||||||
|  |  | ||||||
| // Gauge is a float64 gauge | // Gauge is a float64 gauge | ||||||
| type Gauge interface { | type Gauge interface { | ||||||
|  | 	Add(float64) | ||||||
| 	Get() float64 | 	Get() float64 | ||||||
|  | 	Set(float64) | ||||||
|  | 	Dec() | ||||||
|  | 	Inc() | ||||||
| } | } | ||||||
|  |  | ||||||
| // Histogram is a histogram for non-negative values with automatically created buckets | // Histogram is a histogram for non-negative values with automatically created buckets | ||||||
| @@ -117,6 +125,39 @@ func BuildLabels(labels ...string) []string { | |||||||
| 	return labels | 	return labels | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var spool = newStringsPool(500) | ||||||
|  |  | ||||||
|  | type stringsPool struct { | ||||||
|  | 	p *sync.Pool | ||||||
|  | 	c int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newStringsPool(size int) *stringsPool { | ||||||
|  | 	p := &stringsPool{c: size} | ||||||
|  | 	p.p = &sync.Pool{ | ||||||
|  | 		New: func() interface{} { | ||||||
|  | 			return &strings.Builder{} | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *stringsPool) Cap() int { | ||||||
|  | 	return p.c | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *stringsPool) Get() *strings.Builder { | ||||||
|  | 	return p.p.Get().(*strings.Builder) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *stringsPool) Put(b *strings.Builder) { | ||||||
|  | 	if b.Cap() > p.c { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	b.Reset() | ||||||
|  | 	p.p.Put(b) | ||||||
|  | } | ||||||
|  |  | ||||||
| // BuildName used to combine metric with labels. | // BuildName used to combine metric with labels. | ||||||
| // If labels count is odd, drop last element | // If labels count is odd, drop last element | ||||||
| func BuildName(name string, labels ...string) string { | func BuildName(name string, labels ...string) string { | ||||||
| @@ -125,8 +166,6 @@ func BuildName(name string, labels ...string) string { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(labels) > 2 { | 	if len(labels) > 2 { | ||||||
| 		sort.Sort(byKey(labels)) |  | ||||||
|  |  | ||||||
| 		idx := 0 | 		idx := 0 | ||||||
| 		for { | 		for { | ||||||
| 			if labels[idx] == labels[idx+2] { | 			if labels[idx] == labels[idx+2] { | ||||||
| @@ -141,7 +180,9 @@ func BuildName(name string, labels ...string) string { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var b strings.Builder | 	b := spool.Get() | ||||||
|  | 	defer spool.Put(b) | ||||||
|  |  | ||||||
| 	_, _ = b.WriteString(name) | 	_, _ = b.WriteString(name) | ||||||
| 	_, _ = b.WriteRune('{') | 	_, _ = b.WriteRune('{') | ||||||
| 	for idx := 0; idx < len(labels); idx += 2 { | 	for idx := 0; idx < len(labels); idx += 2 { | ||||||
| @@ -149,8 +190,9 @@ func BuildName(name string, labels ...string) string { | |||||||
| 			_, _ = b.WriteRune(',') | 			_, _ = b.WriteRune(',') | ||||||
| 		} | 		} | ||||||
| 		_, _ = b.WriteString(labels[idx]) | 		_, _ = b.WriteString(labels[idx]) | ||||||
| 		_, _ = b.WriteString(`=`) | 		_, _ = b.WriteString(`="`) | ||||||
| 		_, _ = b.WriteString(strconv.Quote(labels[idx+1])) | 		_, _ = b.WriteString(labels[idx+1]) | ||||||
|  | 		_, _ = b.WriteRune('"') | ||||||
| 	} | 	} | ||||||
| 	_, _ = b.WriteRune('}') | 	_, _ = b.WriteRune('}') | ||||||
|  |  | ||||||
|   | |||||||
| @@ -50,11 +50,12 @@ func TestBuildName(t *testing.T) { | |||||||
| 	data := map[string][]string{ | 	data := map[string][]string{ | ||||||
| 		`my_metric{firstlabel="value2",zerolabel="value3"}`: { | 		`my_metric{firstlabel="value2",zerolabel="value3"}`: { | ||||||
| 			"my_metric", | 			"my_metric", | ||||||
| 			"zerolabel", "value3", "firstlabel", "value2", | 			"firstlabel", "value2", | ||||||
|  | 			"zerolabel", "value3", | ||||||
| 		}, | 		}, | ||||||
| 		`my_metric{broker="broker2",register="mdns",server="tcp"}`: { | 		`my_metric{broker="broker2",register="mdns",server="tcp"}`: { | ||||||
| 			"my_metric", | 			"my_metric", | ||||||
| 			"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns", | 			"broker", "broker1", "broker", "broker2", "register", "mdns", "server", "http", "server", "tcp", | ||||||
| 		}, | 		}, | ||||||
| 		`my_metric{aaa="aaa"}`: { | 		`my_metric{aaa="aaa"}`: { | ||||||
| 			"my_metric", | 			"my_metric", | ||||||
|   | |||||||
| @@ -28,6 +28,10 @@ func (r *noopMeter) Name() string { | |||||||
| 	return r.opts.Name | 	return r.opts.Name | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (r *noopMeter) Unregister(name string, labels ...string) bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
| // Init initialize options | // Init initialize options | ||||||
| func (r *noopMeter) Init(opts ...Option) error { | func (r *noopMeter) Init(opts ...Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| @@ -66,6 +70,11 @@ func (r *noopMeter) Histogram(_ string, labels ...string) Histogram { | |||||||
| 	return &noopHistogram{labels: labels} | 	return &noopHistogram{labels: labels} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // HistogramExt implements the Meter interface | ||||||
|  | func (r *noopMeter) HistogramExt(_ string, quantiles []float64, labels ...string) Histogram { | ||||||
|  | 	return &noopHistogram{labels: labels} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Set implements the Meter interface | // Set implements the Meter interface | ||||||
| func (r *noopMeter) Set(opts ...Option) Meter { | func (r *noopMeter) Set(opts ...Option) Meter { | ||||||
| 	m := &noopMeter{opts: r.opts} | 	m := &noopMeter{opts: r.opts} | ||||||
| @@ -132,6 +141,18 @@ type noopGauge struct { | |||||||
| 	labels []string | 	labels []string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (r *noopGauge) Add(float64) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *noopGauge) Set(float64) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *noopGauge) Inc() { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (r *noopGauge) Dec() { | ||||||
|  | } | ||||||
|  |  | ||||||
| func (r *noopGauge) Get() float64 { | func (r *noopGauge) Get() float64 { | ||||||
| 	return 0 | 	return 0 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,8 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var DefaultQuantiles = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} | ||||||
|  |  | ||||||
| // Option powers the configuration for metrics implementations: | // Option powers the configuration for metrics implementations: | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|  |  | ||||||
| @@ -23,6 +25,8 @@ type Options struct { | |||||||
| 	WriteProcessMetrics bool | 	WriteProcessMetrics bool | ||||||
| 	// WriteFDMetrics flag to write fd metrics | 	// WriteFDMetrics flag to write fd metrics | ||||||
| 	WriteFDMetrics bool | 	WriteFDMetrics bool | ||||||
|  | 	// Quantiles specifies buckets for histogram | ||||||
|  | 	Quantiles []float64 | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions prepares a set of options: | // NewOptions prepares a set of options: | ||||||
| @@ -61,14 +65,12 @@ func Address(value string) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | // Quantiles defines the desired spread of statistics for histogram metrics: | ||||||
| // TimingObjectives defines the desired spread of statistics for histogram / timing metrics: | func Quantiles(quantiles []float64) Option { | ||||||
| func TimingObjectives(value map[float64]float64) Option { |  | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.TimingObjectives = value | 		o.Quantiles = quantiles | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| */ |  | ||||||
|  |  | ||||||
| // Labels add the meter labels | // Labels add the meter labels | ||||||
| func Labels(ls ...string) Option { | func Labels(ls ...string) Option { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user