1 package logging
2
3 import (
4 "bufio"
5 "context"
6 "io"
7
8 "github.com/tetratelabs/wazero/api"
9 "github.com/tetratelabs/wazero/experimental"
10 aslogging "github.com/tetratelabs/wazero/internal/assemblyscript/logging"
11 gologging "github.com/tetratelabs/wazero/internal/gojs/logging"
12 "github.com/tetratelabs/wazero/internal/logging"
13 "github.com/tetratelabs/wazero/internal/wasip1"
14 wasilogging "github.com/tetratelabs/wazero/internal/wasip1/logging"
15 )
16
17 type Writer interface {
18 io.Writer
19 io.StringWriter
20 }
21
22
23
24
25
26
27
28
29
30 type LogScopes = logging.LogScopes
31
32 const (
33
34 LogScopeNone = logging.LogScopeNone
35
36 LogScopeClock = logging.LogScopeClock
37
38
39
40 LogScopeProc = logging.LogScopeProc
41
42
43
44 LogScopeFilesystem = logging.LogScopeFilesystem
45
46
47 LogScopeMemory = logging.LogScopeMemory
48
49 LogScopePoll = logging.LogScopePoll
50
51 LogScopeRandom = logging.LogScopeRandom
52
53 LogScopeSock = logging.LogScopeSock
54
55 LogScopeAll = logging.LogScopeAll
56 )
57
58
59
60
61
62 func NewLoggingListenerFactory(w Writer) experimental.FunctionListenerFactory {
63 return &loggingListenerFactory{w: toInternalWriter(w), scopes: LogScopeAll}
64 }
65
66
67
68
69
70
71
72
73
74
75
76
77 func NewHostLoggingListenerFactory(w Writer, scopes logging.LogScopes) experimental.FunctionListenerFactory {
78 return &loggingListenerFactory{w: toInternalWriter(w), hostOnly: true, scopes: scopes}
79 }
80
81 func toInternalWriter(w Writer) logging.Writer {
82 if w, ok := w.(logging.Writer); ok {
83 return w
84 }
85 return bufio.NewWriter(w)
86 }
87
88 type loggingListenerFactory struct {
89 w logging.Writer
90 hostOnly bool
91 scopes logging.LogScopes
92 stack logStack
93 }
94
95 type flusher interface {
96 Flush() error
97 }
98
99
100
101 func (f *loggingListenerFactory) NewFunctionListener(fnd api.FunctionDefinition) experimental.FunctionListener {
102 exported := len(fnd.ExportNames()) > 0
103 if f.hostOnly &&
104 fnd.GoFunction() == nil &&
105 !exported {
106 return nil
107 }
108
109 var pLoggers []logging.ParamLogger
110 var pSampler logging.ParamSampler
111 var rLoggers []logging.ResultLogger
112 switch fnd.ModuleName() {
113 case wasip1.InternalModuleName:
114 if !wasilogging.IsInLogScope(fnd, f.scopes) {
115 return nil
116 }
117 pSampler, pLoggers, rLoggers = wasilogging.Config(fnd)
118 case "go", "gojs":
119 if !gologging.IsInLogScope(fnd, f.scopes) {
120 return nil
121 }
122 pSampler, pLoggers, rLoggers = gologging.Config(fnd, f.scopes)
123 case "env":
124
125
126 pLoggers, rLoggers = logging.Config(fnd)
127 switch fnd.Name() {
128 case "emscripten_notify_memory_growth":
129 if !logging.LogScopeMemory.IsEnabled(f.scopes) {
130 return nil
131 }
132 default:
133 if !aslogging.IsInLogScope(fnd, f.scopes) {
134 return nil
135 }
136 }
137 default:
138
139 if f.scopes != logging.LogScopeAll {
140 return nil
141 }
142 pLoggers, rLoggers = logging.Config(fnd)
143 }
144
145 var before, after string
146 if fnd.GoFunction() != nil {
147 before = "==> " + fnd.DebugName()
148 after = "<=="
149 } else {
150 before = "--> " + fnd.DebugName()
151 after = "<--"
152 }
153 return &loggingListener{
154 w: f.w,
155 beforePrefix: before,
156 afterPrefix: after,
157 pLoggers: pLoggers,
158 pSampler: pSampler,
159 rLoggers: rLoggers,
160 stack: &f.stack,
161 }
162 }
163
164 type logStack struct {
165 params [][]uint64
166 }
167
168 func (s *logStack) push(params []uint64) {
169 s.params = append(s.params, params)
170 }
171
172 func (s *logStack) pop() []uint64 {
173 i := len(s.params) - 1
174 params := s.params[i]
175 s.params[i] = nil
176 s.params = s.params[:i]
177 return params
178 }
179
180 func (s *logStack) count() (n int) {
181 for _, p := range s.params {
182 if p != nil {
183 n++
184 }
185 }
186 return n
187 }
188
189
190
191 type loggingListener struct {
192 w logging.Writer
193 beforePrefix, afterPrefix string
194 pLoggers []logging.ParamLogger
195 pSampler logging.ParamSampler
196 rLoggers []logging.ResultLogger
197 stack *logStack
198 }
199
200
201
202 func (l *loggingListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, _ experimental.StackIterator) {
203
204 sampled := true
205 if s := l.pSampler; s != nil {
206 sampled = s(ctx, mod, params)
207 }
208
209 if sampled {
210 l.logIndented(l.stack.count(), l.beforePrefix, func() {
211 l.logParams(ctx, mod, params)
212 })
213 params = append([]uint64{}, params...)
214 } else {
215 params = nil
216 }
217
218 l.stack.push(params)
219 }
220
221
222
223 func (l *loggingListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) {
224 if params := l.stack.pop(); params != nil {
225 l.logIndented(l.stack.count(), l.afterPrefix, func() {
226 l.logResults(ctx, mod, params, results)
227 })
228 }
229 }
230
231 func (l *loggingListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, _ error) {
232 l.stack.pop()
233 }
234
235
236
237 func (l *loggingListener) logIndented(nestLevel int, prefix string, log func()) {
238 for i := 0; i < nestLevel; i++ {
239 l.w.WriteByte('\t')
240 }
241 l.w.WriteString(prefix)
242 log()
243 l.w.WriteByte('\n')
244
245 if f, ok := l.w.(flusher); ok {
246 f.Flush()
247 }
248 }
249
250 func (l *loggingListener) logParams(ctx context.Context, mod api.Module, params []uint64) {
251 paramLen := len(l.pLoggers)
252 l.w.WriteByte('(')
253 if paramLen > 0 {
254 l.pLoggers[0](ctx, mod, l.w, params)
255 for i := 1; i < paramLen; i++ {
256 l.w.WriteByte(',')
257 l.pLoggers[i](ctx, mod, l.w, params)
258 }
259 }
260 l.w.WriteByte(')')
261 }
262
263 func (l *loggingListener) logResults(ctx context.Context, mod api.Module, params, results []uint64) {
264 resultLen := len(l.rLoggers)
265 if resultLen == 0 {
266 return
267 }
268 l.w.WriteByte(' ')
269 switch resultLen {
270 case 1:
271 l.rLoggers[0](ctx, mod, l.w, params, results)
272 default:
273 l.w.WriteByte('(')
274 l.rLoggers[0](ctx, mod, l.w, params, results)
275 for i := 1; i < resultLen; i++ {
276 l.w.WriteByte(',')
277 l.rLoggers[i](ctx, mod, l.w, params, results)
278 }
279 l.w.WriteByte(')')
280 }
281 }
282
View as plain text