...

Source file src/github.com/goph/emperror/handler_context.go

Documentation: github.com/goph/emperror

     1  package emperror
     2  
     3  // The implementation bellow is heavily influenced by go-kit's log context.
     4  
     5  // HandlerWith returns a new error handler with keyvals context appended to it.
     6  // If the wrapped error handler is already a contextual error handler created by HandlerWith or HandlerWithPrefix
     7  // keyvals is appended to the existing context, but a new error handler is returned.
     8  //
     9  // The created handler will prepend it's own context to the handled errors.
    10  func HandlerWith(handler Handler, keyvals ...interface{}) Handler {
    11  	if len(keyvals) == 0 {
    12  		return handler
    13  	}
    14  
    15  	kvs, handler := extractHandlerContext(handler)
    16  
    17  	kvs = append(kvs, keyvals...)
    18  
    19  	if len(kvs)%2 != 0 {
    20  		kvs = append(kvs, nil)
    21  	}
    22  
    23  	// Limiting the capacity of the stored keyvals ensures that a new
    24  	// backing array is created if the slice must grow in HandlerWith.
    25  	// Using the extra capacity without copying risks a data race.
    26  	return newContextualHandler(handler, kvs[:len(kvs):len(kvs)])
    27  }
    28  
    29  // HandlerWithPrefix returns a new error handler with keyvals context prepended to it.
    30  // If the wrapped error handler is already a contextual error handler created by HandlerWith or HandlerWithPrefix
    31  // keyvals is prepended to the existing context, but a new error handler is returned.
    32  //
    33  // The created handler will prepend it's own context to the handled errors.
    34  func HandlerWithPrefix(handler Handler, keyvals ...interface{}) Handler {
    35  	if len(keyvals) == 0 {
    36  		return handler
    37  	}
    38  
    39  	prevkvs, handler := extractHandlerContext(handler)
    40  
    41  	n := len(prevkvs) + len(keyvals)
    42  	if len(keyvals)%2 != 0 {
    43  		n++
    44  	}
    45  
    46  	kvs := make([]interface{}, 0, n)
    47  	kvs = append(kvs, keyvals...)
    48  
    49  	if len(kvs)%2 != 0 {
    50  		kvs = append(kvs, nil)
    51  	}
    52  
    53  	kvs = append(kvs, prevkvs...)
    54  
    55  	return newContextualHandler(handler, kvs)
    56  }
    57  
    58  // extractHandlerContext extracts the context and optionally the wrapped handler when it's the same container.
    59  func extractHandlerContext(handler Handler) ([]interface{}, Handler) {
    60  	var kvs []interface{}
    61  
    62  	if c, ok := handler.(*contextualHandler); ok {
    63  		handler = c.handler
    64  		kvs = c.keyvals
    65  	}
    66  
    67  	return kvs, handler
    68  }
    69  
    70  // contextualHandler is a Handler implementation returned by HandlerWith or HandlerWithPrefix.
    71  //
    72  // It wraps an error handler and a holds keyvals as the context.
    73  type contextualHandler struct {
    74  	handler Handler
    75  	keyvals []interface{}
    76  }
    77  
    78  // newContextualHandler creates a new *contextualHandler or a struct which is contextual and holds a stack trace.
    79  func newContextualHandler(handler Handler, kvs []interface{}) Handler {
    80  	chandler := &contextualHandler{
    81  		handler: handler,
    82  		keyvals: kvs,
    83  	}
    84  
    85  	return chandler
    86  }
    87  
    88  // Handle prepends the handler's context to the error's (if any) and delegates the call to the underlying handler.
    89  func (h *contextualHandler) Handle(err error) {
    90  	err = With(err, h.keyvals...)
    91  
    92  	h.handler.Handle(err)
    93  }
    94  

View as plain text