Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
// Package log provides debug logging
package log
import (
"encoding/json"
"fmt"
"time"
)
var (
// Default buffer size if any.
DefaultSize = 1024
// DefaultLog logger.
DefaultLog = NewLog()
// Default formatter.
DefaultFormat = TextFormat
)
// Log is debug log interface for reading and writing logs.
type Log interface {
// Read reads log entries from the logger
Read(...ReadOption) ([]Record, error)
// Write writes records to log
Write(Record) error
// Stream log records
Stream() (Stream, error)
}
// Record is log record entry.
type Record struct {
// Timestamp of logged event
Timestamp time.Time `json:"timestamp"`
// Metadata to enrich log record
Metadata map[string]string `json:"metadata"`
// Value contains log entry
Message interface{} `json:"message"`
}
// Stream returns a log stream.
type Stream interface {
Chan() <-chan Record
Stop() error
}
// Format is a function which formats the output.
type FormatFunc func(Record) string
// TextFormat returns text format.
func TextFormat(r Record) string {
t := r.Timestamp.Format("2006-01-02 15:04:05")
return fmt.Sprintf("%s %v", t, r.Message)
}
// JSONFormat is a json Format func.
func JSONFormat(r Record) string {
b, _ := json.Marshal(r)
return string(b)
}
+70
View File
@@ -0,0 +1,70 @@
package log
import "time"
// Option used by the logger.
type Option func(*Options)
// Options are logger options.
type Options struct {
// Format specifies the output format
Format FormatFunc
// Name of the log
Name string
// Size is the size of ring buffer
Size int
}
// Name of the log.
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Size sets the size of the ring buffer.
func Size(s int) Option {
return func(o *Options) {
o.Size = s
}
}
func Format(f FormatFunc) Option {
return func(o *Options) {
o.Format = f
}
}
// DefaultOptions returns default options.
func DefaultOptions() Options {
return Options{
Size: DefaultSize,
}
}
// ReadOptions for querying the logs.
type ReadOptions struct {
// Since what time in past to return the logs
Since time.Time
// Count specifies number of logs to return
Count int
// Stream requests continuous log stream
Stream bool
}
// ReadOption used for reading the logs.
type ReadOption func(*ReadOptions)
// Since sets the time since which to return the log records.
func Since(s time.Time) ReadOption {
return func(o *ReadOptions) {
o.Since = s
}
}
// Count sets the number of log records to return.
func Count(c int) ReadOption {
return func(o *ReadOptions) {
o.Count = c
}
}
+81
View File
@@ -0,0 +1,81 @@
package log
import (
"sync"
"github.com/google/uuid"
"go-micro.dev/v4/util/ring"
)
// Should stream from OS.
type osLog struct {
format FormatFunc
buffer *ring.Buffer
subs map[string]*osStream
sync.RWMutex
once sync.Once
}
type osStream struct {
stream chan Record
}
// Read reads log entries from the logger.
func (o *osLog) Read(...ReadOption) ([]Record, error) {
var records []Record
// read the last 100 records
for _, v := range o.buffer.Get(100) {
records = append(records, v.Value.(Record))
}
return records, nil
}
// Write writes records to log.
func (o *osLog) Write(r Record) error {
o.buffer.Put(r)
return nil
}
// Stream log records.
func (o *osLog) Stream() (Stream, error) {
o.Lock()
defer o.Unlock()
// create stream
st := &osStream{
stream: make(chan Record, 128),
}
// save stream
o.subs[uuid.New().String()] = st
return st, nil
}
func (o *osStream) Chan() <-chan Record {
return o.stream
}
func (o *osStream) Stop() error {
return nil
}
func NewLog(opts ...Option) Log {
options := Options{
Format: DefaultFormat,
}
for _, o := range opts {
o(&options)
}
l := &osLog{
format: options.Format,
buffer: ring.New(1024),
subs: make(map[string]*osStream),
}
return l
}
@@ -0,0 +1,78 @@
// Package http enables the http profiler
package http
import (
"context"
"net/http"
"net/http/pprof"
"sync"
"go-micro.dev/v4/debug/profile"
)
type httpProfile struct {
server *http.Server
sync.Mutex
running bool
}
var (
DefaultAddress = ":6060"
)
// Start the profiler.
func (h *httpProfile) Start() error {
h.Lock()
defer h.Unlock()
if h.running {
return nil
}
go func() {
if err := h.server.ListenAndServe(); err != nil {
h.Lock()
h.running = false
h.Unlock()
}
}()
h.running = true
return nil
}
// Stop the profiler.
func (h *httpProfile) Stop() error {
h.Lock()
defer h.Unlock()
if !h.running {
return nil
}
h.running = false
return h.server.Shutdown(context.TODO())
}
func (h *httpProfile) String() string {
return "http"
}
func NewProfile(opts ...profile.Option) profile.Profile {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
return &httpProfile{
server: &http.Server{
Addr: DefaultAddress,
Handler: mux,
},
}
}
@@ -0,0 +1,98 @@
// Package pprof provides a pprof profiler
package pprof
import (
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"sync"
"go-micro.dev/v4/debug/profile"
)
type profiler struct {
// where the cpu profile is written
cpuFile *os.File
// where the mem profile is written
memFile *os.File
opts profile.Options
sync.Mutex
running bool
}
func (p *profiler) Start() error {
p.Lock()
defer p.Unlock()
if p.running {
return nil
}
cpuFile := filepath.Join(os.TempDir(), "cpu.pprof")
memFile := filepath.Join(os.TempDir(), "mem.pprof")
if len(p.opts.Name) > 0 {
cpuFile = filepath.Join(os.TempDir(), p.opts.Name+".cpu.pprof")
memFile = filepath.Join(os.TempDir(), p.opts.Name+".mem.pprof")
}
f1, err := os.Create(cpuFile)
if err != nil {
return err
}
f2, err := os.Create(memFile)
if err != nil {
return err
}
// start cpu profiling
if err := pprof.StartCPUProfile(f1); err != nil {
return err
}
// set cpu file
p.cpuFile = f1
// set mem file
p.memFile = f2
p.running = true
return nil
}
func (p *profiler) Stop() error {
p.Lock()
defer p.Unlock()
if !p.running {
return nil
}
pprof.StopCPUProfile()
p.cpuFile.Close()
runtime.GC()
pprof.WriteHeapProfile(p.memFile)
p.memFile.Close()
p.running = false
p.cpuFile = nil
p.memFile = nil
return nil
}
func (p *profiler) String() string {
return "pprof"
}
func NewProfile(opts ...profile.Option) profile.Profile {
var options profile.Options
for _, o := range opts {
o(&options)
}
p := new(profiler)
p.opts = options
return p
}
+43
View File
@@ -0,0 +1,43 @@
// Package profile is for profilers
package profile
type Profile interface {
// Start the profiler
Start() error
// Stop the profiler
Stop() error
// Name of the profiler
String() string
}
var (
DefaultProfile Profile = new(noop)
)
type noop struct{}
func (p *noop) Start() error {
return nil
}
func (p *noop) Stop() error {
return nil
}
func (p *noop) String() string {
return "noop"
}
type Options struct {
// Name to use for the profile
Name string
}
type Option func(o *Options)
// Name of the profile.
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
+89
View File
@@ -0,0 +1,89 @@
package trace
import (
"context"
"time"
"github.com/google/uuid"
"go-micro.dev/v4/util/ring"
)
type memTracer struct {
// ring buffer of traces
buffer *ring.Buffer
opts Options
}
func (t *memTracer) Read(opts ...ReadOption) ([]*Span, error) {
var options ReadOptions
for _, o := range opts {
o(&options)
}
sp := t.buffer.Get(t.buffer.Size())
spans := make([]*Span, 0, len(sp))
for _, span := range sp {
val := span.Value.(*Span)
// skip if trace id is specified and doesn't match
if len(options.Trace) > 0 && val.Trace != options.Trace {
continue
}
spans = append(spans, val)
}
return spans, nil
}
func (t *memTracer) Start(ctx context.Context, name string) (context.Context, *Span) {
span := &Span{
Name: name,
Trace: uuid.New().String(),
Id: uuid.New().String(),
Started: time.Now(),
Metadata: make(map[string]string),
}
// return span if no context
if ctx == nil {
return ToContext(context.Background(), span.Trace, span.Id), span
}
traceID, parentSpanID, ok := FromContext(ctx)
// If the trace can not be found in the header,
// that means this is where the trace is created.
if !ok {
return ToContext(ctx, span.Trace, span.Id), span
}
// set trace id
span.Trace = traceID
// set parent
span.Parent = parentSpanID
// return the span
return ToContext(ctx, span.Trace, span.Id), span
}
func (t *memTracer) Finish(s *Span) error {
// set finished time
s.Duration = time.Since(s.Started)
// save the span
t.buffer.Put(s)
return nil
}
func NewTracer(opts ...Option) Tracer {
var options Options
for _, o := range opts {
o(&options)
}
return &memTracer{
opts: options,
// the last 256 requests
buffer: ring.New(256),
}
}
+21
View File
@@ -0,0 +1,21 @@
package trace
import "context"
type noop struct{}
func (n *noop) Init(...Option) error {
return nil
}
func (n *noop) Start(ctx context.Context, name string) (context.Context, *Span) {
return nil, nil
}
func (n *noop) Finish(*Span) error {
return nil
}
func (n *noop) Read(...ReadOption) ([]*Span, error) {
return nil, nil
}
+34
View File
@@ -0,0 +1,34 @@
package trace
type Options struct {
// Size is the size of ring buffer
Size int
}
type Option func(o *Options)
type ReadOptions struct {
// Trace id
Trace string
}
type ReadOption func(o *ReadOptions)
// Read the given trace.
func ReadTrace(t string) ReadOption {
return func(o *ReadOptions) {
o.Trace = t
}
}
const (
// DefaultSize of the buffer.
DefaultSize = 64
)
// DefaultOptions returns default options.
func DefaultOptions() Options {
return Options{
Size: DefaultSize,
}
}
+82
View File
@@ -0,0 +1,82 @@
// Package trace provides an interface for distributed tracing
package trace
import (
"context"
"time"
"go-micro.dev/v4/metadata"
"go-micro.dev/v4/transport/headers"
)
var (
// DefaultTracer is the default tracer.
DefaultTracer = NewTracer()
)
// Tracer is an interface for distributed tracing.
type Tracer interface {
// Start a trace
Start(ctx context.Context, name string) (context.Context, *Span)
// Finish the trace
Finish(*Span) error
// Read the traces
Read(...ReadOption) ([]*Span, error)
}
// SpanType describe the nature of the trace span.
type SpanType int
const (
// SpanTypeRequestInbound is a span created when serving a request.
SpanTypeRequestInbound SpanType = iota
// SpanTypeRequestOutbound is a span created when making a service call.
SpanTypeRequestOutbound
)
// Span is used to record an entry.
type Span struct {
// Start time
Started time.Time
// associated data
Metadata map[string]string
// Id of the trace
Trace string
// name of the span
Name string
// id of the span
Id string
// parent span id
Parent string
// Duration in nano seconds
Duration time.Duration
// Type
Type SpanType
}
// FromContext returns a span from context.
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
traceID, traceOk := metadata.Get(ctx, headers.TraceIDKey)
microID, microOk := metadata.Get(ctx, headers.ID)
if !traceOk && !microOk {
isFound = false
return
}
if !traceOk {
traceID = microID
}
parentSpanID, ok := metadata.Get(ctx, headers.SpanID)
return traceID, parentSpanID, ok
}
// ToContext saves the trace and span ids in the context.
func ToContext(ctx context.Context, traceID, parentSpanID string) context.Context {
return metadata.MergeContext(ctx, map[string]string{
headers.TraceIDKey: traceID,
headers.SpanID: parentSpanID,
}, true)
}