@@ -151,7 +151,11 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
151
151
// command via the ProxyCommand SSH option.
152
152
pid := os .Getppid ()
153
153
154
- logger := inv .Logger
154
+ // Use a stripped down writer that doesn't sync, otherwise you get
155
+ // "failed to sync sloghuman: sync /dev/stderr: The handle is
156
+ // invalid" on Windows. Syncing isn't required for stdout/stderr
157
+ // anyways.
158
+ logger := inv .Logger .AppendSinks (sloghuman .Sink (slogWriter {w : inv .Stderr })).Leveled (slog .LevelDebug )
155
159
if logDir != "" {
156
160
logFilePath := filepath .Join (logDir , fmt .Sprintf ("%d.log" , pid ))
157
161
logFile , err := fs .OpenFile (logFilePath , os .O_CREATE | os .O_WRONLY , 0o600 )
@@ -160,7 +164,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
160
164
}
161
165
dc := cliutil .DiscardAfterClose (logFile )
162
166
defer dc .Close ()
163
- logger = logger .AppendSinks (sloghuman .Sink (dc )). Leveled ( slog . LevelDebug )
167
+ logger = logger .AppendSinks (sloghuman .Sink (dc ))
164
168
}
165
169
if r .disableDirect {
166
170
logger .Info (ctx , "direct connections disabled" )
@@ -204,31 +208,48 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
204
208
// command via the ProxyCommand SSH option.
205
209
networkInfoFilePath := filepath .Join (networkInfoDir , fmt .Sprintf ("%d.json" , pid ))
206
210
207
- statsErrChan := make (chan error , 1 )
211
+ var (
212
+ firstErrTime time.Time
213
+ errCh = make (chan error , 1 )
214
+ )
208
215
cb := func (start , end time.Time , virtual , _ map [netlogtype.Connection ]netlogtype.Counts ) {
209
- sendErr := func (err error ) {
216
+ sendErr := func (tolerate bool , err error ) {
217
+ logger .Error (ctx , "collect network stats" , slog .Error (err ))
218
+ // Tolerate up to 1 minute of errors.
219
+ if tolerate {
220
+ if firstErrTime .IsZero () {
221
+ logger .Info (ctx , "tolerating network stats errors for up to 1 minute" )
222
+ firstErrTime = time .Now ()
223
+ }
224
+ if time .Since (firstErrTime ) < time .Minute {
225
+ return
226
+ }
227
+ }
228
+
210
229
select {
211
- case statsErrChan <- err :
230
+ case errCh <- err :
212
231
default :
213
232
}
214
233
}
215
234
216
235
stats , err := collectNetworkStats (ctx , agentConn , start , end , virtual )
217
236
if err != nil {
218
- sendErr (err )
237
+ sendErr (true , err )
219
238
return
220
239
}
221
240
222
241
rawStats , err := json .Marshal (stats )
223
242
if err != nil {
224
- sendErr (err )
243
+ sendErr (false , err )
225
244
return
226
245
}
227
246
err = afero .WriteFile (fs , networkInfoFilePath , rawStats , 0o600 )
228
247
if err != nil {
229
- sendErr (err )
248
+ sendErr (false , err )
230
249
return
231
250
}
251
+
252
+ firstErrTime = time.Time {}
232
253
}
233
254
234
255
now := time .Now ()
@@ -238,7 +259,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
238
259
select {
239
260
case <- ctx .Done ():
240
261
return nil
241
- case err := <- statsErrChan :
262
+ case err := <- errCh :
242
263
return err
243
264
}
244
265
},
@@ -280,6 +301,18 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
280
301
return cmd
281
302
}
282
303
304
+ // slogWriter wraps an io.Writer and removes all other methods (such as Sync),
305
+ // which may cause undesired/broken behavior.
306
+ type slogWriter struct {
307
+ w io.Writer
308
+ }
309
+
310
+ var _ io.Writer = slogWriter {}
311
+
312
+ func (s slogWriter ) Write (p []byte ) (n int , err error ) {
313
+ return s .w .Write (p )
314
+ }
315
+
283
316
type sshNetworkStats struct {
284
317
P2P bool `json:"p2p"`
285
318
Latency float64 `json:"latency"`
0 commit comments