WIP: Save agent roles integration work before CHORUS rebrand
- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										400
									
								
								vendor/github.com/ipfs/go-log/v2/setup.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								vendor/github.com/ipfs/go-log/v2/setup.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,400 @@ | ||||
| package log | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/mattn/go-isatty" | ||||
| 	"go.uber.org/zap" | ||||
| 	"go.uber.org/zap/zapcore" | ||||
| ) | ||||
|  | ||||
| var config Config | ||||
|  | ||||
| func init() { | ||||
| 	SetupLogging(configFromEnv()) | ||||
| } | ||||
|  | ||||
| // Logging environment variables | ||||
| const ( | ||||
| 	// IPFS_* prefixed env vars kept for backwards compatibility | ||||
| 	// for this release. They will not be available in the next | ||||
| 	// release. | ||||
| 	// | ||||
| 	// GOLOG_* env vars take precedences over IPFS_* env vars. | ||||
| 	envIPFSLogging    = "IPFS_LOGGING" | ||||
| 	envIPFSLoggingFmt = "IPFS_LOGGING_FMT" | ||||
|  | ||||
| 	envLogging    = "GOLOG_LOG_LEVEL" | ||||
| 	envLoggingFmt = "GOLOG_LOG_FMT" | ||||
|  | ||||
| 	envLoggingFile = "GOLOG_FILE" // /path/to/file | ||||
| 	envLoggingURL  = "GOLOG_URL"  // url that will be processed by sink in the zap | ||||
|  | ||||
| 	envLoggingOutput = "GOLOG_OUTPUT"     // possible values: stdout|stderr|file combine multiple values with '+' | ||||
| 	envLoggingLabels = "GOLOG_LOG_LABELS" // comma-separated key-value pairs, i.e. "app=example_app,dc=sjc-1" | ||||
| ) | ||||
|  | ||||
| type LogFormat int | ||||
|  | ||||
| const ( | ||||
| 	ColorizedOutput LogFormat = iota | ||||
| 	PlaintextOutput | ||||
| 	JSONOutput | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	// Format overrides the format of the log output. Defaults to ColorizedOutput | ||||
| 	Format LogFormat | ||||
|  | ||||
| 	// Level is the default minimum enabled logging level. | ||||
| 	Level LogLevel | ||||
|  | ||||
| 	// SubsystemLevels are the default levels per-subsystem. When unspecified, defaults to Level. | ||||
| 	SubsystemLevels map[string]LogLevel | ||||
|  | ||||
| 	// Stderr indicates whether logs should be written to stderr. | ||||
| 	Stderr bool | ||||
|  | ||||
| 	// Stdout indicates whether logs should be written to stdout. | ||||
| 	Stdout bool | ||||
|  | ||||
| 	// File is a path to a file that logs will be written to. | ||||
| 	File string | ||||
|  | ||||
| 	// URL with schema supported by zap. Use zap.RegisterSink | ||||
| 	URL string | ||||
|  | ||||
| 	// Labels is a set of key-values to apply to all loggers | ||||
| 	Labels map[string]string | ||||
| } | ||||
|  | ||||
| // ErrNoSuchLogger is returned when the util pkg is asked for a non existant logger | ||||
| var ErrNoSuchLogger = errors.New("error: No such logger") | ||||
|  | ||||
| var loggerMutex sync.RWMutex // guards access to global logger state | ||||
|  | ||||
| // loggers is the set of loggers in the system | ||||
| var loggers = make(map[string]*zap.SugaredLogger) | ||||
| var levels = make(map[string]zap.AtomicLevel) | ||||
|  | ||||
| // primaryFormat is the format of the primary core used for logging | ||||
| var primaryFormat LogFormat = ColorizedOutput | ||||
|  | ||||
| // defaultLevel is the default log level | ||||
| var defaultLevel LogLevel = LevelError | ||||
|  | ||||
| // primaryCore is the primary logging core | ||||
| var primaryCore zapcore.Core | ||||
|  | ||||
| // loggerCore is the base for all loggers created by this package | ||||
| var loggerCore = &lockedMultiCore{} | ||||
|  | ||||
| // GetConfig returns a copy of the saved config. It can be inspected, modified, | ||||
| // and re-applied using a subsequent call to SetupLogging(). | ||||
| func GetConfig() Config { | ||||
| 	return config | ||||
| } | ||||
|  | ||||
| // SetupLogging will initialize the logger backend and set the flags. | ||||
| // TODO calling this in `init` pushes all configuration to env variables | ||||
| // - move it out of `init`? then we need to change all the code (js-ipfs, go-ipfs) to call this explicitly | ||||
| // - have it look for a config file? need to define what that is | ||||
| func SetupLogging(cfg Config) { | ||||
| 	loggerMutex.Lock() | ||||
| 	defer loggerMutex.Unlock() | ||||
|  | ||||
| 	config = cfg | ||||
|  | ||||
| 	primaryFormat = cfg.Format | ||||
| 	defaultLevel = cfg.Level | ||||
|  | ||||
| 	outputPaths := []string{} | ||||
|  | ||||
| 	if cfg.Stderr { | ||||
| 		outputPaths = append(outputPaths, "stderr") | ||||
| 	} | ||||
| 	if cfg.Stdout { | ||||
| 		outputPaths = append(outputPaths, "stdout") | ||||
| 	} | ||||
|  | ||||
| 	// check if we log to a file | ||||
| 	if len(cfg.File) > 0 { | ||||
| 		if path, err := normalizePath(cfg.File); err != nil { | ||||
| 			fmt.Fprintf(os.Stderr, "failed to resolve log path '%q', logging to %s\n", cfg.File, outputPaths) | ||||
| 		} else { | ||||
| 			outputPaths = append(outputPaths, path) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(cfg.URL) > 0 { | ||||
| 		outputPaths = append(outputPaths, cfg.URL) | ||||
| 	} | ||||
|  | ||||
| 	ws, _, err := zap.Open(outputPaths...) | ||||
| 	if err != nil { | ||||
| 		panic(fmt.Sprintf("unable to open logging output: %v", err)) | ||||
| 	} | ||||
|  | ||||
| 	newPrimaryCore := newCore(primaryFormat, ws, LevelDebug) // the main core needs to log everything. | ||||
|  | ||||
| 	for k, v := range cfg.Labels { | ||||
| 		newPrimaryCore = newPrimaryCore.With([]zap.Field{zap.String(k, v)}) | ||||
| 	} | ||||
|  | ||||
| 	setPrimaryCore(newPrimaryCore) | ||||
| 	setAllLoggers(defaultLevel) | ||||
|  | ||||
| 	for name, level := range cfg.SubsystemLevels { | ||||
| 		if leveler, ok := levels[name]; ok { | ||||
| 			leveler.SetLevel(zapcore.Level(level)) | ||||
| 		} else { | ||||
| 			levels[name] = zap.NewAtomicLevelAt(zapcore.Level(level)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // SetPrimaryCore changes the primary logging core. If the SetupLogging was | ||||
| // called then the previously configured core will be replaced. | ||||
| func SetPrimaryCore(core zapcore.Core) { | ||||
| 	loggerMutex.Lock() | ||||
| 	defer loggerMutex.Unlock() | ||||
|  | ||||
| 	setPrimaryCore(core) | ||||
| } | ||||
|  | ||||
| func setPrimaryCore(core zapcore.Core) { | ||||
| 	if primaryCore != nil { | ||||
| 		loggerCore.ReplaceCore(primaryCore, core) | ||||
| 	} else { | ||||
| 		loggerCore.AddCore(core) | ||||
| 	} | ||||
| 	primaryCore = core | ||||
| } | ||||
|  | ||||
| // SetDebugLogging calls SetAllLoggers with logging.DEBUG | ||||
| func SetDebugLogging() { | ||||
| 	SetAllLoggers(LevelDebug) | ||||
| } | ||||
|  | ||||
| // SetAllLoggers changes the logging level of all loggers to lvl | ||||
| func SetAllLoggers(lvl LogLevel) { | ||||
| 	loggerMutex.RLock() | ||||
| 	defer loggerMutex.RUnlock() | ||||
|  | ||||
| 	setAllLoggers(lvl) | ||||
| } | ||||
|  | ||||
| func setAllLoggers(lvl LogLevel) { | ||||
| 	for _, l := range levels { | ||||
| 		l.SetLevel(zapcore.Level(lvl)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // SetLogLevel changes the log level of a specific subsystem | ||||
| // name=="*" changes all subsystems | ||||
| func SetLogLevel(name, level string) error { | ||||
| 	lvl, err := LevelFromString(level) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// wildcard, change all | ||||
| 	if name == "*" { | ||||
| 		SetAllLoggers(lvl) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	loggerMutex.RLock() | ||||
| 	defer loggerMutex.RUnlock() | ||||
|  | ||||
| 	// Check if we have a logger by that name | ||||
| 	if _, ok := levels[name]; !ok { | ||||
| 		return ErrNoSuchLogger | ||||
| 	} | ||||
|  | ||||
| 	levels[name].SetLevel(zapcore.Level(lvl)) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // SetLogLevelRegex sets all loggers to level `l` that match expression `e`. | ||||
| // An error is returned if `e` fails to compile. | ||||
| func SetLogLevelRegex(e, l string) error { | ||||
| 	lvl, err := LevelFromString(l) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	rem, err := regexp.Compile(e) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	loggerMutex.Lock() | ||||
| 	defer loggerMutex.Unlock() | ||||
| 	for name := range loggers { | ||||
| 		if rem.MatchString(name) { | ||||
| 			levels[name].SetLevel(zapcore.Level(lvl)) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // GetSubsystems returns a slice containing the | ||||
| // names of the current loggers | ||||
| func GetSubsystems() []string { | ||||
| 	loggerMutex.RLock() | ||||
| 	defer loggerMutex.RUnlock() | ||||
| 	subs := make([]string, 0, len(loggers)) | ||||
|  | ||||
| 	for k := range loggers { | ||||
| 		subs = append(subs, k) | ||||
| 	} | ||||
| 	return subs | ||||
| } | ||||
|  | ||||
| func getLogger(name string) *zap.SugaredLogger { | ||||
| 	loggerMutex.Lock() | ||||
| 	defer loggerMutex.Unlock() | ||||
| 	log, ok := loggers[name] | ||||
| 	if !ok { | ||||
| 		level, ok := levels[name] | ||||
| 		if !ok { | ||||
| 			level = zap.NewAtomicLevelAt(zapcore.Level(defaultLevel)) | ||||
| 			levels[name] = level | ||||
| 		} | ||||
| 		log = zap.New(loggerCore). | ||||
| 			WithOptions( | ||||
| 				zap.IncreaseLevel(level), | ||||
| 				zap.AddCaller(), | ||||
| 			). | ||||
| 			Named(name). | ||||
| 			Sugar() | ||||
|  | ||||
| 		loggers[name] = log | ||||
| 	} | ||||
|  | ||||
| 	return log | ||||
| } | ||||
|  | ||||
| // configFromEnv returns a Config with defaults populated using environment variables. | ||||
| func configFromEnv() Config { | ||||
| 	cfg := Config{ | ||||
| 		Format:          ColorizedOutput, | ||||
| 		Stderr:          true, | ||||
| 		Level:           LevelError, | ||||
| 		SubsystemLevels: map[string]LogLevel{}, | ||||
| 		Labels:          map[string]string{}, | ||||
| 	} | ||||
|  | ||||
| 	format := os.Getenv(envLoggingFmt) | ||||
| 	if format == "" { | ||||
| 		format = os.Getenv(envIPFSLoggingFmt) | ||||
| 	} | ||||
|  | ||||
| 	var noExplicitFormat bool | ||||
|  | ||||
| 	switch format { | ||||
| 	case "color": | ||||
| 		cfg.Format = ColorizedOutput | ||||
| 	case "nocolor": | ||||
| 		cfg.Format = PlaintextOutput | ||||
| 	case "json": | ||||
| 		cfg.Format = JSONOutput | ||||
| 	default: | ||||
| 		if format != "" { | ||||
| 			fmt.Fprintf(os.Stderr, "ignoring unrecognized log format '%s'\n", format) | ||||
| 		} | ||||
| 		noExplicitFormat = true | ||||
| 	} | ||||
|  | ||||
| 	lvl := os.Getenv(envLogging) | ||||
| 	if lvl == "" { | ||||
| 		lvl = os.Getenv(envIPFSLogging) | ||||
| 	} | ||||
| 	if lvl != "" { | ||||
| 		for _, kvs := range strings.Split(lvl, ",") { | ||||
| 			kv := strings.SplitN(kvs, "=", 2) | ||||
| 			lvl, err := LevelFromString(kv[len(kv)-1]) | ||||
| 			if err != nil { | ||||
| 				fmt.Fprintf(os.Stderr, "error setting log level %q: %s\n", kvs, err) | ||||
| 				continue | ||||
| 			} | ||||
| 			switch len(kv) { | ||||
| 			case 1: | ||||
| 				cfg.Level = lvl | ||||
| 			case 2: | ||||
| 				cfg.SubsystemLevels[kv[0]] = lvl | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	cfg.File = os.Getenv(envLoggingFile) | ||||
| 	// Disable stderr logging when a file is specified | ||||
| 	// https://github.com/ipfs/go-log/issues/83 | ||||
| 	if cfg.File != "" { | ||||
| 		cfg.Stderr = false | ||||
| 	} | ||||
|  | ||||
| 	cfg.URL = os.Getenv(envLoggingURL) | ||||
| 	output := os.Getenv(envLoggingOutput) | ||||
| 	outputOptions := strings.Split(output, "+") | ||||
| 	for _, opt := range outputOptions { | ||||
| 		switch opt { | ||||
| 		case "stdout": | ||||
| 			cfg.Stdout = true | ||||
| 		case "stderr": | ||||
| 			cfg.Stderr = true | ||||
| 		case "file": | ||||
| 			if cfg.File == "" { | ||||
| 				fmt.Fprint(os.Stderr, "please specify a GOLOG_FILE value to write to") | ||||
| 			} | ||||
| 		case "url": | ||||
| 			if cfg.URL == "" { | ||||
| 				fmt.Fprint(os.Stderr, "please specify a GOLOG_URL value to write to") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check that neither of the requested Std* nor the file are TTYs | ||||
| 	// At this stage (configFromEnv) we do not have a uniform list to examine yet | ||||
| 	if noExplicitFormat && | ||||
| 		!(cfg.Stdout && isTerm(os.Stdout)) && | ||||
| 		!(cfg.Stderr && isTerm(os.Stderr)) && | ||||
| 		// check this last: expensive | ||||
| 		!(cfg.File != "" && pathIsTerm(cfg.File)) { | ||||
| 		cfg.Format = PlaintextOutput | ||||
| 	} | ||||
|  | ||||
| 	labels := os.Getenv(envLoggingLabels) | ||||
| 	if labels != "" { | ||||
| 		labelKVs := strings.Split(labels, ",") | ||||
| 		for _, label := range labelKVs { | ||||
| 			kv := strings.Split(label, "=") | ||||
| 			if len(kv) != 2 { | ||||
| 				fmt.Fprint(os.Stderr, "invalid label k=v: ", label) | ||||
| 				continue | ||||
| 			} | ||||
| 			cfg.Labels[kv[0]] = kv[1] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return cfg | ||||
| } | ||||
|  | ||||
| func isTerm(f *os.File) bool { | ||||
| 	return isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd()) | ||||
| } | ||||
|  | ||||
| func pathIsTerm(p string) bool { | ||||
| 	// !!!no!!! O_CREAT, if we fail - we fail | ||||
| 	f, err := os.OpenFile(p, os.O_WRONLY, 0) | ||||
| 	if f != nil { | ||||
| 		defer f.Close() // nolint:errcheck | ||||
| 	} | ||||
| 	return err == nil && isTerm(f) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins