github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/program_logger.go (about) 1 package environment 2 3 import ( 4 "time" 5 6 "github.com/onflow/cadence/runtime/common" 7 "github.com/rs/zerolog" 8 "go.opentelemetry.io/otel/attribute" 9 otelTrace "go.opentelemetry.io/otel/trace" 10 11 "github.com/onflow/flow-go/fvm/tracing" 12 "github.com/onflow/flow-go/module/trace" 13 ) 14 15 // MetricsReporter captures and reports metrics to back to the execution 16 // environment it is a setup passed to the context. 17 type MetricsReporter interface { 18 RuntimeTransactionParsed(time.Duration) 19 RuntimeTransactionChecked(time.Duration) 20 RuntimeTransactionInterpreted(time.Duration) 21 RuntimeSetNumberOfAccounts(count uint64) 22 RuntimeTransactionProgramsCacheMiss() 23 RuntimeTransactionProgramsCacheHit() 24 } 25 26 // NoopMetricsReporter is a MetricReporter that does nothing. 27 type NoopMetricsReporter struct{} 28 29 // RuntimeTransactionParsed is a noop 30 func (NoopMetricsReporter) RuntimeTransactionParsed(time.Duration) {} 31 32 // RuntimeTransactionChecked is a noop 33 func (NoopMetricsReporter) RuntimeTransactionChecked(time.Duration) {} 34 35 // RuntimeTransactionInterpreted is a noop 36 func (NoopMetricsReporter) RuntimeTransactionInterpreted(time.Duration) {} 37 38 // RuntimeSetNumberOfAccounts is a noop 39 func (NoopMetricsReporter) RuntimeSetNumberOfAccounts(count uint64) {} 40 41 // RuntimeTransactionProgramsCacheMiss is a noop 42 func (NoopMetricsReporter) RuntimeTransactionProgramsCacheMiss() {} 43 44 // RuntimeTransactionProgramsCacheHit is a noop 45 func (NoopMetricsReporter) RuntimeTransactionProgramsCacheHit() {} 46 47 type ProgramLoggerParams struct { 48 zerolog.Logger 49 50 CadenceLoggingEnabled bool 51 52 MetricsReporter 53 } 54 55 func DefaultProgramLoggerParams() ProgramLoggerParams { 56 return ProgramLoggerParams{ 57 Logger: zerolog.Nop(), 58 CadenceLoggingEnabled: false, 59 MetricsReporter: NoopMetricsReporter{}, 60 } 61 } 62 63 type ProgramLogger struct { 64 tracer tracing.TracerSpan 65 66 ProgramLoggerParams 67 68 logs []string 69 } 70 71 func NewProgramLogger( 72 tracer tracing.TracerSpan, 73 params ProgramLoggerParams, 74 ) *ProgramLogger { 75 return &ProgramLogger{ 76 tracer: tracer, 77 ProgramLoggerParams: params, 78 logs: nil, 79 } 80 } 81 82 func (logger *ProgramLogger) Logger() zerolog.Logger { 83 return logger.ProgramLoggerParams.Logger 84 } 85 86 func (logger *ProgramLogger) ImplementationDebugLog(message string) error { 87 logger.Debug().Msgf("Cadence: %s", message) 88 return nil 89 } 90 91 func (logger *ProgramLogger) ProgramLog(message string) error { 92 defer logger.tracer.StartExtensiveTracingChildSpan( 93 trace.FVMEnvProgramLog).End() 94 95 if logger.CadenceLoggingEnabled { 96 97 // If cadence logging is enabled (which is usually in the 98 // emulator or emulator based tools), 99 // we log the message to the zerolog logger so that they can be tracked 100 // while stepping through a transaction/script. 101 logger. 102 Debug(). 103 Msgf("Cadence log: %s", message) 104 105 logger.logs = append(logger.logs, message) 106 } 107 return nil 108 } 109 110 func (logger *ProgramLogger) Logs() []string { 111 return logger.logs 112 } 113 114 func (logger *ProgramLogger) RecordTrace( 115 operation string, 116 location common.Location, 117 duration time.Duration, 118 attrs []attribute.KeyValue, 119 ) { 120 if location != nil { 121 attrs = append(attrs, attribute.String("location", location.String())) 122 } 123 124 end := time.Now() 125 126 span := logger.tracer.StartChildSpan( 127 trace.FVMCadenceTrace.Child(operation), 128 otelTrace.WithAttributes(attrs...), 129 otelTrace.WithTimestamp(end.Add(-duration))) 130 span.End(otelTrace.WithTimestamp(end)) 131 } 132 133 // ProgramParsed captures time spent on parsing a code at specific location 134 func (logger *ProgramLogger) ProgramParsed( 135 location common.Location, 136 duration time.Duration, 137 ) { 138 logger.RecordTrace("parseProgram", location, duration, nil) 139 140 // These checks prevent re-reporting durations, the metrics collection is 141 // a bit counter-intuitive: 142 // The three functions (parsing, checking, interpretation) are not called 143 // in sequence, but in some cases as part of each other. We basically only 144 // measure the durations reported for the entry-point (the transaction), 145 // and not for child locations, because they might be already part of the 146 // duration for the entry-point. 147 if location == nil { 148 return 149 } 150 if _, ok := location.(common.TransactionLocation); ok { 151 logger.MetricsReporter.RuntimeTransactionParsed(duration) 152 } 153 } 154 155 // ProgramChecked captures time spent on checking a code at specific location 156 func (logger *ProgramLogger) ProgramChecked( 157 location common.Location, 158 duration time.Duration, 159 ) { 160 logger.RecordTrace("checkProgram", location, duration, nil) 161 162 // see the comment for ProgramParsed 163 if location == nil { 164 return 165 } 166 if _, ok := location.(common.TransactionLocation); ok { 167 logger.MetricsReporter.RuntimeTransactionChecked(duration) 168 } 169 } 170 171 // ProgramInterpreted captures time spent on interpreting a code at specific location 172 func (logger *ProgramLogger) ProgramInterpreted( 173 location common.Location, 174 duration time.Duration, 175 ) { 176 logger.RecordTrace("interpretProgram", location, duration, nil) 177 178 // see the comment for ProgramInterpreted 179 if location == nil { 180 return 181 } 182 if _, ok := location.(common.TransactionLocation); ok { 183 logger.MetricsReporter.RuntimeTransactionInterpreted(duration) 184 } 185 } 186 187 // ValueEncoded accumulates time spend on runtime value encoding 188 func (logger *ProgramLogger) ValueEncoded(duration time.Duration) { 189 logger.RecordTrace("encodeValue", nil, duration, nil) 190 } 191 192 // ValueDecoded accumulates time spend on runtime value decoding 193 func (logger *ProgramLogger) ValueDecoded(duration time.Duration) { 194 logger.RecordTrace("decodeValue", nil, duration, nil) 195 }