1// Copyright 2016 VMware, Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package trace 16 17import ( 18 "bytes" 19 "context" 20 "fmt" 21 "os" 22 "sync/atomic" 23 "time" 24 25 "github.com/sirupsen/logrus" 26 27 "github.com/vmware/govmomi/vim25/types" 28) 29 30type OperationKey string 31 32const OpTraceKey OperationKey = "traceKey" 33 34var opIDPrefix = os.Getpid() 35 36// opCount is a monotonic counter which increments on Start() 37var opCount uint64 38 39type Operation struct { 40 context.Context 41 operation 42 43 // Logger is used to configure an Operation-specific destination for log messages, in addition 44 // to the global logger. This logger is passed to any children which are created. 45 Logger *logrus.Logger 46} 47 48type operation struct { 49 t []Message 50 id string 51 // TODO: might want to do this via a Formatter, but just testing here 52 indent string 53} 54 55func newOperation(ctx context.Context, id string, skip int, msg string) *Operation { 56 op := operation{ 57 58 // Can be used to trace based on this number which is unique per chain 59 // of operations 60 id: id, 61 } 62 63 o := &Operation{ 64 Context: ctx, 65 operation: op, 66 } 67 68 // Start the trace. 69 o.operation.t = []Message{*newTrace(msg, skip, o)} 70 71 // We need to be able to identify this operation across API (and process) 72 // boundaries. So add the trace as a value to the embedded context. We 73 // stash the values individually in the context because we can't assign 74 // the operation itself as a value to the embedded context (it's circular) 75 ctx = context.WithValue(ctx, OpTraceKey, op) 76 77 // By adding the op.id any operations passed to govmomi will result 78 // in the op.id being logged in vSphere (vpxa / hostd) as the prefix to opID 79 // For example if the op.id was 299.16 hostd would show 80 // verbose hostd[12281B70] [Originator@6876 sub=PropertyProvider opID=299.16-5b05 user=root] 81 ctx = context.WithValue(ctx, types.ID{}, op.id) 82 83 return o 84} 85 86// Creates a header string to be printed. 87func (o *Operation) header() string { 88 return fmt.Sprintf("op=%s", o.id) 89} 90 91// Err returns a non-nil error value after Done is closed. Err returns 92// Canceled if the context was canceled or DeadlineExceeded if the 93// context's deadline passed. No other values for Err are defined. 94// After Done is closed, successive calls to Err return the same value. 95func (o Operation) Err() error { 96 97 // Walk up the contexts from which this context was created and get their errors 98 if err := o.Context.Err(); err != nil { 99 buf := &bytes.Buffer{} 100 101 // Add a frame for this Err call, then walk the stack 102 currFrame := newTrace("Err", 2, &o) 103 fmt.Fprintf(buf, "%s: %s error: %s\n", currFrame.funcName, o.t[0].msg, err) 104 105 // handle the carriage return 106 numFrames := len(o.t) 107 108 for i, t := range o.t { 109 fmt.Fprintf(buf, "%-15s:%d %s", t.funcName, t.lineNo, t.msg) 110 111 // don't add a cr on the last frame 112 if i != numFrames-1 { 113 buf.WriteByte('\n') 114 } 115 } 116 117 // Print the error 118 o.Errorf(buf.String()) 119 120 return err 121 } 122 123 return nil 124} 125 126func (o Operation) String() string { 127 return o.header() 128} 129 130func (o *Operation) ID() string { 131 return o.id 132} 133 134func (o *Operation) Auditf(format string, args ...interface{}) { 135 o.Infof(format, args...) 136} 137 138func (o *Operation) Infof(format string, args ...interface{}) { 139 o.Info(fmt.Sprintf(format, args...)) 140} 141 142func (o *Operation) Info(args ...interface{}) { 143 msg := fmt.Sprint(args...) 144 145 Logger.Infof("%s: %s%s", o.header(), o.indent, msg) 146 147 if o.Logger != nil { 148 o.Logger.Info(msg) 149 } 150} 151 152func (o *Operation) Debugf(format string, args ...interface{}) { 153 o.Debug(fmt.Sprintf(format, args...)) 154} 155 156func (o *Operation) Debug(args ...interface{}) { 157 msg := fmt.Sprint(args...) 158 159 Logger.Debugf("%s: %s%s", o.header(), o.indent, msg) 160 161 if o.Logger != nil { 162 o.Logger.Debug(msg) 163 } 164} 165 166func (o *Operation) Warnf(format string, args ...interface{}) { 167 o.Warn(fmt.Sprintf(format, args...)) 168} 169 170func (o *Operation) Warn(args ...interface{}) { 171 msg := fmt.Sprint(args...) 172 173 Logger.Warnf("%s: %s%s", o.header(), o.indent, msg) 174 175 if o.Logger != nil { 176 o.Logger.Warn(msg) 177 } 178} 179 180func (o *Operation) Errorf(format string, args ...interface{}) { 181 o.Error(fmt.Sprintf(format, args...)) 182} 183 184func (o *Operation) Error(args ...interface{}) { 185 msg := fmt.Sprint(args...) 186 187 Logger.Errorf("%s: %s%s", o.header(), o.indent, msg) 188 189 if o.Logger != nil { 190 o.Logger.Error(msg) 191 } 192} 193 194func (o *Operation) Panicf(format string, args ...interface{}) { 195 o.Panic(fmt.Sprintf(format, args...)) 196} 197 198func (o *Operation) Panic(args ...interface{}) { 199 msg := fmt.Sprint(args...) 200 201 Logger.Panicf("%s: %s%s", o.header(), o.indent, msg) 202 203 if o.Logger != nil { 204 o.Logger.Panic(msg) 205 } 206} 207 208func (o *Operation) Fatalf(format string, args ...interface{}) { 209 o.Fatal(fmt.Sprintf(format, args...)) 210} 211 212func (o *Operation) Fatal(args ...interface{}) { 213 msg := fmt.Sprint(args...) 214 215 Logger.Fatalf("%s: %s%s", o.header(), o.indent, msg) 216 217 if o.Logger != nil { 218 o.Logger.Fatal(msg) 219 } 220} 221 222func (o *Operation) newChildCommon(ctx context.Context, opID string, msg string) *Operation { 223 child := newOperation(ctx, opID, 5, msg) 224 child.t = append(child.t, o.t...) 225 child.Logger = o.Logger 226 // increase the indent 227 child.indent = o.indent + " " 228 229 o.Debugf("[ChildOperation: %s] %s", child.id, msg) 230 return child 231} 232 233func (o *Operation) newChild(ctx context.Context, msg string) *Operation { 234 return o.newChildCommon(ctx, opID(atomic.AddUint64(&opCount, 1)), msg) 235} 236 237func (o *Operation) newChildWithChainedID(ctx context.Context, msg string) *Operation { 238 childOpID := fmt.Sprintf("%s.%d", o.id, atomic.AddUint64(&opCount, 1)) 239 return o.newChildCommon(ctx, childOpID, msg) 240} 241 242func opID(opNum uint64) string { 243 return fmt.Sprintf("%d.%d", opIDPrefix, opNum) 244} 245 246// NewOperation will return a new operation with operationID added as a value to the context 247func NewOperation(ctx context.Context, format string, args ...interface{}) Operation { 248 return *decoratedNewOperation(ctx, "NewOperation", format, args...) 249} 250 251func decoratedNewOperation(ctx context.Context, decoration, format string, args ...interface{}) *Operation { 252 detail := fmt.Sprintf(format, args...) 253 o := newOperation(ctx, opID(atomic.AddUint64(&opCount, 1)), 4, detail) 254 255 frame := o.t[0] 256 o.Debugf("[%s][%s:%d] %s", decoration, frame.funcName, frame.lineNo, detail) 257 return o 258} 259 260// NewOperationFromID returns a an Operation with the incoming ID if valid 261// It creates a parent operation with the incoming ID and a child with 262// the parent operation ID as a prefix and a monotonically incremented 263// integer as the suffix 264func NewOperationFromID(ctx context.Context, ID *string, format string, args ...interface{}) Operation { 265 var o Operation 266 if ID == nil || *ID == "" { 267 o = *newOperation(ctx, opID(atomic.AddUint64(&opCount, 1)), 3, fmt.Sprintf(format, args...)) 268 } else { 269 msg := fmt.Sprintf(format, args...) 270 o = *newOperation(ctx, *ID, 3, msg).newChildWithChainedID(ctx, msg) 271 } 272 273 frame := o.t[0] 274 o.Debugf("[NewOperationFromID] %s [%s:%d]", o.header(), frame.funcName, frame.lineNo) 275 return o 276} 277 278// NewOperationWithLoggerFrom will return a new operation with operationID added as a value to the 279// context and logging settings copied from the supplied operation. 280// 281// Deprecated: This method was added to aid in converting old code to use operation-based logging. 282// Its use almost always indicates a broken context/operation model (e.g., a context 283// being improperly stored in a struct instead of being passed between functions). 284func NewOperationWithLoggerFrom(ctx context.Context, oldOp Operation, format string, args ...interface{}) Operation { 285 op := NewOperation(ctx, format, args...) 286 op.Logger = oldOp.Logger 287 return op 288} 289 290// WithTimeout creates a new operation from parent with context.WithTimeout 291func WithTimeout(parent *Operation, timeout time.Duration, format string, args ...interface{}) (Operation, context.CancelFunc) { 292 ctx, cancelFunc := context.WithTimeout(parent.Context, timeout) 293 op := parent.newChild(ctx, fmt.Sprintf(format, args...)) 294 295 return *op, cancelFunc 296} 297 298// WithDeadline creates a new operation from parent with context.WithDeadline 299func WithDeadline(parent *Operation, expiration time.Time, format string, args ...interface{}) (Operation, context.CancelFunc) { 300 ctx, cancelFunc := context.WithDeadline(parent.Context, expiration) 301 op := parent.newChild(ctx, fmt.Sprintf(format, args...)) 302 303 return *op, cancelFunc 304} 305 306// WithCancel creates a new operation from parent with context.WithCancel 307func WithCancel(parent *Operation, format string, args ...interface{}) (Operation, context.CancelFunc) { 308 ctx, cancelFunc := context.WithCancel(parent.Context) 309 op := parent.newChild(ctx, fmt.Sprintf(format, args...)) 310 311 return *op, cancelFunc 312} 313 314// WithValue creates a new operation from parent with context.WithValue 315func WithValue(parent *Operation, key, val interface{}, format string, args ...interface{}) Operation { 316 ctx := context.WithValue(parent.Context, key, val) 317 op := parent.newChild(ctx, fmt.Sprintf(format, args...)) 318 319 return *op 320} 321 322// FromOperation creates a child operation from the one supplied 323// uses the same context as the parent 324func FromOperation(parent Operation, format string, args ...interface{}) Operation { 325 return *parent.newChild(parent.Context, fmt.Sprintf(format, args...)) 326} 327 328// fromContext will return an Operation 329// 330// The Operation returned will be one of the following: 331// The operation in the context value 332// The operation passed as the context param 333// nil if neither of the above are true 334func fromContext(ctx context.Context) *Operation { 335 336 // do we have an operation - if we do we're creating a new child from the one in the context 337 // While we could just use it directly we'd be losing the presumably valuable message 338 if op, ok := ctx.(Operation); ok { 339 return &op 340 } 341 342 // do we have a context w/the op added as a value 343 op, ok := ctx.Value(OpTraceKey).(operation) 344 if !ok || op.id == "" { 345 return nil 346 } 347 348 // return an operation based off the context value 349 return &Operation{ 350 Context: ctx, 351 operation: op, 352 } 353} 354 355// FromContext will return an Operation. The message will only be used in the case of 356// a new Operation being created, i.e. there was no operation found in the context 357// 358// The Operation returned will be one of the following: 359// The operation in the context value 360// The operation passed as the context param 361// A new operation 362func FromContext(ctx context.Context, message string, args ...interface{}) Operation { 363 cop := fromContext(ctx) 364 if cop != nil { 365 return *cop 366 } 367 368 return *decoratedNewOperation(ctx, "NewOperationFromContext", message, args...) 369} 370 371// ChildFromContext will return an Operation 372// 373// The Operation returned will be one of the following: 374// A child of the operation in the context value 375// A child of the operation passed as the context param 376// A new operation 377func ChildFromContext(ctx context.Context, message string, args ...interface{}) Operation { 378 379 // do we have an operation - if we do we're creating a new child from the one in the context 380 // While we could just use it directly we'd be losing the presumably valuable message 381 parent := fromContext(ctx) 382 if parent != nil { 383 return *parent.newChild(parent.Context, fmt.Sprintf(message, args...)) 384 } 385 386 return *decoratedNewOperation(ctx, "NewChildFromContext", message, args...) 387} 388