+35
util/cliutil/ipfslog.go
+35
util/cliutil/ipfslog.go
···
1
+
package cliutil
2
+
3
+
import (
4
+
"io"
5
+
6
+
ipfslog "github.com/ipfs/go-log/v2"
7
+
"go.uber.org/zap/zapcore"
8
+
)
9
+
10
+
func SetIpfsWriter(out io.Writer, format string, level string) {
11
+
var ze zapcore.Encoder
12
+
switch format {
13
+
case "json":
14
+
ze = zapcore.NewJSONEncoder(zapcore.EncoderConfig{})
15
+
case "text":
16
+
ze = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{})
17
+
default:
18
+
ze = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{})
19
+
}
20
+
var zl zapcore.LevelEnabler
21
+
switch level {
22
+
case "debug":
23
+
zl = zapcore.DebugLevel
24
+
case "info":
25
+
zl = zapcore.InfoLevel
26
+
case "warn":
27
+
zl = zapcore.WarnLevel
28
+
case "error":
29
+
zl = zapcore.ErrorLevel
30
+
default:
31
+
zl = zapcore.InfoLevel
32
+
}
33
+
nc := zapcore.NewCore(ze, zapcore.AddSync(out), zl)
34
+
ipfslog.SetPrimaryCore(nc)
35
+
}
+46
-16
util/cliutil/util.go
+46
-16
util/cliutil/util.go
···
260
260
//
261
261
// passing default cliutil.LogOptions{} is ok.
262
262
//
263
-
// GOLOG_LOG_LEVEL=info|debug|warn|error
263
+
// BSKYLOG_LOG_LEVEL=info|debug|warn|error
264
264
//
265
-
// GOLOG_LOG_FMT=text|json
265
+
// BSKYLOG_LOG_FMT=text|json
266
266
//
267
-
// GOLOG_FILE=path (or "-" or "" for stdout), %T gets UnixMilli; if a path with '/', {prefix}/current becomes a link to active log file
267
+
// BSKYLOG_FILE=path (or "-" or "" for stdout), %T gets UnixMilli; if a path with '/', {prefix}/current becomes a link to active log file
268
268
//
269
-
// GOLOG_ROTATE_BYTES=int maximum size of log chunk before rotating
269
+
// BSKYLOG_ROTATE_BYTES=int maximum size of log chunk before rotating
270
270
//
271
-
// GOLOG_ROTATE_KEEP=int keep N olg logs (not including current)
271
+
// BSKYLOG_ROTATE_KEEP=int keep N olg logs (not including current)
272
272
//
273
273
// (env vars derived from ipfs logging library)
274
274
func SetupSlog(options LogOptions) (*slog.Logger, error) {
275
+
fmt.Fprintf(os.Stderr, "SetupSlog\n")
275
276
var hopts slog.HandlerOptions
276
277
hopts.Level = slog.LevelInfo
277
278
hopts.AddSource = true
278
279
if options.LogLevel == "" {
279
-
options.LogLevel = os.Getenv("GOLOG_LOG_LEVEL")
280
+
options.LogLevel = os.Getenv("BSKYLOG_LOG_LEVEL")
280
281
}
281
282
if options.LogLevel == "" {
282
283
hopts.Level = slog.LevelInfo
···
297
298
}
298
299
}
299
300
if options.LogFormat == "" {
300
-
options.LogFormat = os.Getenv("GOLOG_LOG_FMT")
301
+
options.LogFormat = os.Getenv("BSKYLOG_LOG_FMT")
301
302
}
302
303
if options.LogFormat == "" {
303
304
options.LogFormat = "text"
···
312
313
}
313
314
314
315
if options.LogPath == "" {
315
-
options.LogPath = os.Getenv("GOLOG_FILE")
316
+
options.LogPath = os.Getenv("BSKYLOG_FILE")
316
317
}
317
318
if options.LogRotateBytes == 0 {
318
-
rotateBytesStr := os.Getenv("GOLOG_ROTATE_BYTES")
319
+
rotateBytesStr := os.Getenv("BSKYLOG_ROTATE_BYTES")
319
320
if rotateBytesStr != "" {
320
321
rotateBytes, err := strconv.ParseInt(rotateBytesStr, 10, 64)
321
322
if err != nil {
322
-
return nil, fmt.Errorf("invalid GOLOG_ROTATE_BYTES value: %w", err)
323
+
return nil, fmt.Errorf("invalid BSKYLOG_ROTATE_BYTES value: %w", err)
323
324
}
324
325
options.LogRotateBytes = rotateBytes
325
326
}
326
327
}
327
328
if options.KeepOld == 0 {
328
329
keepOldUnset := true
329
-
keepOldStr := os.Getenv("GOLOG_ROTATE_KEEP")
330
+
keepOldStr := os.Getenv("BSKYLOG_ROTATE_KEEP")
330
331
if keepOldStr != "" {
331
332
keepOld, err := strconv.ParseInt(keepOldStr, 10, 64)
332
333
if err != nil {
333
-
return nil, fmt.Errorf("invalid GOLOG_ROTATE_KEEP value: %w", err)
334
+
return nil, fmt.Errorf("invalid BSKYLOG_ROTATE_KEEP value: %w", err)
334
335
}
335
336
keepOldUnset = false
336
337
options.KeepOld = int(keepOld)
···
339
340
options.KeepOld = 2
340
341
}
341
342
}
343
+
logaround := make(chan string, 100)
344
+
go logbouncer(logaround)
342
345
var out io.Writer
343
346
if (options.LogPath == "") || (options.LogPath == "-") {
344
347
out = os.Stdout
···
347
350
rotateBytes: options.LogRotateBytes,
348
351
outPathTemplate: options.LogPath,
349
352
keep: options.KeepOld,
353
+
logaround: logaround,
350
354
}
351
355
} else {
352
356
var err error
···
354
358
if err != nil {
355
359
return nil, fmt.Errorf("%s: %w", options.LogPath, err)
356
360
}
361
+
fmt.Fprintf(os.Stderr, "SetupSlog create %#v\n", options.LogPath)
357
362
}
358
363
var handler slog.Handler
359
364
switch options.LogFormat {
···
366
371
}
367
372
logger := slog.New(handler)
368
373
slog.SetDefault(logger)
374
+
templateDirPart, _ := filepath.Split(options.LogPath)
375
+
ents, _ := os.ReadDir(templateDirPart)
376
+
for _, ent := range ents {
377
+
fmt.Fprintf(os.Stdout, "%s\n", filepath.Join(templateDirPart, ent.Name()))
378
+
}
379
+
SetIpfsWriter(out, options.LogFormat, options.LogLevel)
369
380
return logger, nil
370
381
}
371
382
···
387
398
388
399
// keep the most recent N log files (not including current)
389
400
keep int
401
+
402
+
// write strings to this from inside the log system, a task outside the log system hands them to slog.Info()
403
+
logaround chan<- string
404
+
}
405
+
406
+
func logbouncer(out <-chan string) {
407
+
var logger *slog.Logger
408
+
for line := range out {
409
+
fmt.Fprintf(os.Stderr, "ll %s\n", line)
410
+
if logger == nil {
411
+
// lazy to make sure it crops up after slog Default has been set
412
+
logger = slog.Default().With("system", "logging")
413
+
}
414
+
logger.Info(line)
415
+
}
390
416
}
391
417
392
418
var currentMatcher = regexp.MustCompile("current_\\d+")
···
401
427
// find old logs
402
428
templateDirPart, templateNamePart := filepath.Split(w.outPathTemplate)
403
429
if dirpart != templateDirPart {
404
-
fmt.Fprintf(os.Stderr, "current dir part %#v != template dir part %#v\n", w.currentPath, w.outPathTemplate)
430
+
w.logaround <- fmt.Sprintf("current dir part %#v != template dir part %#v\n", w.currentPath, w.outPathTemplate)
405
431
return
406
432
}
407
433
// build a regexp that is string literal parts with \d+ replacing the UnixMilli part
···
418
444
}
419
445
tmre, err := regexp.Compile(sb.String())
420
446
if err != nil {
421
-
fmt.Fprintf(os.Stderr, "failed to compile old log template regexp: %#v\n", err)
447
+
w.logaround <- fmt.Sprintf("failed to compile old log template regexp: %#v\n", err)
422
448
return
423
449
}
424
450
dir, err := os.ReadDir(dirpart)
425
451
if err != nil {
426
-
fmt.Fprintf(os.Stderr, "failed to read old log template dir: %#v\n", err)
452
+
w.logaround <- fmt.Sprintf("failed to read old log template dir: %#v\n", err)
427
453
return
428
454
}
429
455
var found []fs.FileInfo
···
450
476
fullpath := filepath.Join(dirpart, fi.Name())
451
477
err = os.Remove(fullpath)
452
478
if err != nil {
453
-
fmt.Fprintf(os.Stderr, "failed to rm old log: %#v\n", err)
479
+
w.logaround <- fmt.Sprintf("failed to rm old log: %#v\n", err)
454
480
// but keep going
455
481
}
456
482
// maybe it would be safe to debug-log old log removal from within the logging infrastructure?
···
489
515
earlyWeakErrors = append(earlyWeakErrors, err)
490
516
return errors.Join(earlyWeakErrors...), nil
491
517
}
518
+
w.logaround <- fmt.Sprintf("new log file %#v", w.currentPath)
492
519
w.cleanOldLogs()
493
520
dirpart, _ := filepath.Split(w.currentPath)
494
521
if dirpart != "" {
···
534
561
if err != nil {
535
562
earlyWeakErrors = append(earlyWeakErrors, err)
536
563
return wrote, errors.Join(earlyWeakErrors...)
564
+
}
565
+
if earlyWeakErrors != nil {
566
+
w.logaround <- fmt.Sprintf("ok, but: %s", errors.Join(earlyWeakErrors...).Error())
537
567
}
538
568
return wrote, nil
539
569
}