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