Skip to content

Commit 7ec84f8

Browse files
committed
log: create mechanism for tracing code regions
Add a 'Region()' to the 'TraceLogger' interface that starts logging a region for a given context, created an updated context with region metadata (time of region start, nesting level), and returns a corresponding "end region" function used for closing out the region. Implement 'Region()' for the 'Trace2' logger. Example of adding region tracing to a function 'func myFunc()': --- func myFunc(ctx context.Context, logger log.TraceLogger) { ctx, endRegion := logger.Region(ctx, "my_file", "my_func") defer endRegion() ...the rest of myFunc()... } --- While 'endRegion()' can be called manually before each possible 'return', using 'defer' ensures that the region will exit properly from all 'return's as well as 'panic'. Signed-off-by: Victoria Dye <[email protected]>
1 parent e093c80 commit 7ec84f8

File tree

2 files changed

+71
-10
lines changed

2 files changed

+71
-10
lines changed

internal/log/logger.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
type loggedError error
1313

1414
type TraceLogger interface {
15+
Region(ctx context.Context, category string, label string) (context.Context, func())
1516
LogCommand(ctx context.Context, commandName string) context.Context
1617
Error(ctx context.Context, err error) error
1718
Errorf(ctx context.Context, format string, a ...any) error

internal/log/trace2.go

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ type ctxKey int
3232

3333
const (
3434
sidId ctxKey = iota
35+
parentRegionId
3536
)
3637

38+
type trace2Region struct {
39+
level int
40+
tStart time.Time
41+
}
42+
3743
type Trace2 struct {
3844
logger *zap.Logger
3945
}
@@ -105,24 +111,51 @@ func (l fieldList) withTime() fieldList {
105111
return append(l, zap.Float64("t_abs", time.Since(globalStart).Seconds()))
106112
}
107113

114+
func (l fieldList) withNesting(r trace2Region, includeTRel bool) fieldList {
115+
l = append(l, zap.Int("nesting", r.level))
116+
if includeTRel {
117+
l = append(l, zap.Float64("t_rel", time.Since(r.tStart).Seconds()))
118+
}
119+
return l
120+
}
121+
108122
func (l fieldList) with(f ...zap.Field) fieldList {
109123
return append(l, f...)
110124
}
111125

126+
func getContextValue[T any](
127+
ctx context.Context,
128+
key ctxKey,
129+
) (bool, T) {
130+
var value T
131+
haveValue := false
132+
valueAny := ctx.Value(key)
133+
if valueAny != nil {
134+
value, haveValue = valueAny.(T)
135+
}
136+
return haveValue, value
137+
}
138+
139+
func getOrSetContextValue[T any](
140+
ctx context.Context,
141+
key ctxKey,
142+
newValueFunc func() T,
143+
) (context.Context, T) {
144+
var value T
145+
haveValue, value := getContextValue[T](ctx, key)
146+
if !haveValue {
147+
value = newValueFunc()
148+
ctx = context.WithValue(ctx, key, value)
149+
}
150+
151+
return ctx, value
152+
}
153+
112154
func (t *Trace2) sharedFields(ctx context.Context) (context.Context, fieldList) {
113155
fields := fieldList{}
114156

115157
// Get the session ID
116-
var sid uuid.UUID
117-
haveSid := false
118-
sidAny := ctx.Value(sidId)
119-
if sidAny != nil {
120-
sid, haveSid = sidAny.(uuid.UUID)
121-
}
122-
if !haveSid {
123-
sid = uuid.New()
124-
ctx = context.WithValue(ctx, sidId, sid)
125-
}
158+
ctx, sid := getOrSetContextValue(ctx, sidId, uuid.New)
126159
fields = append(fields, zap.String("sid", sid.String()))
127160

128161
// Hardcode the thread to "main" because Go doesn't like to share its
@@ -166,6 +199,33 @@ func (t *Trace2) logExit(ctx context.Context, exitCode int) {
166199
t.logger.Sync()
167200
}
168201

202+
func (t *Trace2) Region(ctx context.Context, category string, label string) (context.Context, func()) {
203+
ctx, sharedFields := t.sharedFields(ctx)
204+
205+
// Get the nesting level & increment
206+
hasParentRegion, nesting := getContextValue[trace2Region](ctx, parentRegionId)
207+
if !hasParentRegion {
208+
nesting = trace2Region{
209+
level: 0,
210+
tStart: time.Now(),
211+
}
212+
} else {
213+
nesting.level++
214+
nesting.tStart = time.Now()
215+
}
216+
ctx = context.WithValue(ctx, parentRegionId, nesting)
217+
218+
regionFields := fieldList{
219+
zap.String("category", category),
220+
zap.String("label", label),
221+
}
222+
223+
t.logger.Debug("region_enter", sharedFields.withNesting(nesting, false).with(regionFields...)...)
224+
return ctx, func() {
225+
t.logger.Debug("region_leave", sharedFields.withNesting(nesting, true).with(regionFields...)...)
226+
}
227+
}
228+
169229
func (t *Trace2) LogCommand(ctx context.Context, commandName string) context.Context {
170230
ctx, sharedFields := t.sharedFields(ctx)
171231

0 commit comments

Comments
 (0)