...

Source file src/github.com/datawire/ambassador/v2/pkg/debug/debug.go

Documentation: github.com/datawire/ambassador/v2/pkg/debug

     1  package debug
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  )
    12  
    13  // This struct serves as the root of all runtime debug info for the process. This consists of timers
    14  // that aggregate timing info for various kinds of actions, as well as atomic values that can be
    15  // updated as relevant state changes.
    16  type Debug struct {
    17  	mutex  sync.Mutex        // Protects the whole debug struct.
    18  	timers map[string]*Timer // Holds the debug timers.
    19  	values map[string]*Value // holds the debug values.
    20  
    21  	clock ClockFunc // clock function to pass to all the timers
    22  }
    23  
    24  // An atomic.Value with custom json marshalling.
    25  type Value atomic.Value
    26  
    27  func (v *Value) MarshalJSON() ([]byte, error) {
    28  	return json.MarshalIndent((*atomic.Value)(v).Load(), "", "  ")
    29  }
    30  
    31  // The default debug root.
    32  var root = NewDebug()
    33  
    34  // A key for use with context values.
    35  var key = &struct{}{}
    36  
    37  // The NewContext function creates a child context associated with the given Debug root.
    38  func NewContext(parent context.Context, debug *Debug) context.Context {
    39  	return context.WithValue(parent, key, debug)
    40  }
    41  
    42  // The FromContext function retrieves the correct debug root for the given context.
    43  func FromContext(ctx context.Context) (result *Debug) {
    44  	value := ctx.Value(key)
    45  	if value != nil {
    46  		result = value.(*Debug)
    47  	} else {
    48  		result = root
    49  	}
    50  	return
    51  }
    52  
    53  // Create a new set of debug info.
    54  func NewDebug() *Debug {
    55  	return NewDebugWithClock(time.Now)
    56  }
    57  
    58  // Create a new set of debug info with the specified clock function.
    59  func NewDebugWithClock(clock ClockFunc) *Debug {
    60  	return &Debug{clock: clock, timers: map[string]*Timer{}, values: map[string]*Value{}}
    61  }
    62  
    63  // Access the contexts of the debug info while holding the mutex.
    64  func (d *Debug) withMutex(f func()) {
    65  	d.mutex.Lock()
    66  	defer d.mutex.Unlock()
    67  	f()
    68  }
    69  
    70  // The Timer() method ensures the named timer exists and returns it.
    71  func (d *Debug) Timer(name string) (result *Timer) {
    72  	d.withMutex(func() {
    73  		var ok bool
    74  		result, ok = d.timers[name]
    75  		if !ok {
    76  			result = NewTimerWithClock(d.clock)
    77  			d.timers[name] = result
    78  		}
    79  	})
    80  	return
    81  }
    82  
    83  // The Value() method ensures the named atomic.Value exists and returns it.
    84  func (d *Debug) Value(name string) (result *atomic.Value) {
    85  	d.withMutex(func() {
    86  		r, ok := d.values[name]
    87  		if !ok {
    88  			r = &Value{}
    89  			d.values[name] = r
    90  		}
    91  		result = (*atomic.Value)(r)
    92  	})
    93  	return
    94  }
    95  
    96  // The ServeHTTP() method will serve a json representation of the contents of the debug root.
    97  func (d *Debug) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    98  	d.withMutex(func() {
    99  		bytes, err := json.MarshalIndent(map[string]interface{}{
   100  			"timers": d.timers,
   101  			"values": d.values,
   102  		}, "", "  ")
   103  
   104  		if err != nil {
   105  			http.Error(w, fmt.Sprintf("error marshalling debug info: %v", err), http.StatusInternalServerError)
   106  		} else {
   107  			_, _ = w.Write(append(bytes, '\n'))
   108  		}
   109  	})
   110  }
   111  

View as plain text