 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2016 Uber Technologies, Inc.
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| package zap
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"sort"
 | |
| 	"time"
 | |
| 
 | |
| 	"go.uber.org/zap/zapcore"
 | |
| )
 | |
| 
 | |
| // SamplingConfig sets a sampling strategy for the logger. Sampling caps the
 | |
| // global CPU and I/O load that logging puts on your process while attempting
 | |
| // to preserve a representative subset of your logs.
 | |
| //
 | |
| // If specified, the Sampler will invoke the Hook after each decision.
 | |
| //
 | |
| // Values configured here are per-second. See zapcore.NewSamplerWithOptions for
 | |
| // details.
 | |
| type SamplingConfig struct {
 | |
| 	Initial    int                                           `json:"initial" yaml:"initial"`
 | |
| 	Thereafter int                                           `json:"thereafter" yaml:"thereafter"`
 | |
| 	Hook       func(zapcore.Entry, zapcore.SamplingDecision) `json:"-" yaml:"-"`
 | |
| }
 | |
| 
 | |
| // Config offers a declarative way to construct a logger. It doesn't do
 | |
| // anything that can't be done with New, Options, and the various
 | |
| // zapcore.WriteSyncer and zapcore.Core wrappers, but it's a simpler way to
 | |
| // toggle common options.
 | |
| //
 | |
| // Note that Config intentionally supports only the most common options. More
 | |
| // unusual logging setups (logging to network connections or message queues,
 | |
| // splitting output between multiple files, etc.) are possible, but require
 | |
| // direct use of the zapcore package. For sample code, see the package-level
 | |
| // BasicConfiguration and AdvancedConfiguration examples.
 | |
| //
 | |
| // For an example showing runtime log level changes, see the documentation for
 | |
| // AtomicLevel.
 | |
| type Config struct {
 | |
| 	// Level is the minimum enabled logging level. Note that this is a dynamic
 | |
| 	// level, so calling Config.Level.SetLevel will atomically change the log
 | |
| 	// level of all loggers descended from this config.
 | |
| 	Level AtomicLevel `json:"level" yaml:"level"`
 | |
| 	// Development puts the logger in development mode, which changes the
 | |
| 	// behavior of DPanicLevel and takes stacktraces more liberally.
 | |
| 	Development bool `json:"development" yaml:"development"`
 | |
| 	// DisableCaller stops annotating logs with the calling function's file
 | |
| 	// name and line number. By default, all logs are annotated.
 | |
| 	DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
 | |
| 	// DisableStacktrace completely disables automatic stacktrace capturing. By
 | |
| 	// default, stacktraces are captured for WarnLevel and above logs in
 | |
| 	// development and ErrorLevel and above in production.
 | |
| 	DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
 | |
| 	// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
 | |
| 	Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
 | |
| 	// Encoding sets the logger's encoding. Valid values are "json" and
 | |
| 	// "console", as well as any third-party encodings registered via
 | |
| 	// RegisterEncoder.
 | |
| 	Encoding string `json:"encoding" yaml:"encoding"`
 | |
| 	// EncoderConfig sets options for the chosen encoder. See
 | |
| 	// zapcore.EncoderConfig for details.
 | |
| 	EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
 | |
| 	// OutputPaths is a list of URLs or file paths to write logging output to.
 | |
| 	// See Open for details.
 | |
| 	OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
 | |
| 	// ErrorOutputPaths is a list of URLs to write internal logger errors to.
 | |
| 	// The default is standard error.
 | |
| 	//
 | |
| 	// Note that this setting only affects internal errors; for sample code that
 | |
| 	// sends error-level logs to a different location from info- and debug-level
 | |
| 	// logs, see the package-level AdvancedConfiguration example.
 | |
| 	ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
 | |
| 	// InitialFields is a collection of fields to add to the root logger.
 | |
| 	InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
 | |
| }
 | |
| 
 | |
| // NewProductionEncoderConfig returns an opinionated EncoderConfig for
 | |
| // production environments.
 | |
| //
 | |
| // Messages encoded with this configuration will be JSON-formatted
 | |
| // and will have the following keys by default:
 | |
| //
 | |
| //   - "level": The logging level (e.g. "info", "error").
 | |
| //   - "ts": The current time in number of seconds since the Unix epoch.
 | |
| //   - "msg": The message passed to the log statement.
 | |
| //   - "caller": If available, a short path to the file and line number
 | |
| //     where the log statement was issued.
 | |
| //     The logger configuration determines whether this field is captured.
 | |
| //   - "stacktrace": If available, a stack trace from the line
 | |
| //     where the log statement was issued.
 | |
| //     The logger configuration determines whether this field is captured.
 | |
| //
 | |
| // By default, the following formats are used for different types:
 | |
| //
 | |
| //   - Time is formatted as floating-point number of seconds since the Unix
 | |
| //     epoch.
 | |
| //   - Duration is formatted as floating-point number of seconds.
 | |
| //
 | |
| // You may change these by setting the appropriate fields in the returned
 | |
| // object.
 | |
| // For example, use the following to change the time encoding format:
 | |
| //
 | |
| //	cfg := zap.NewProductionEncoderConfig()
 | |
| //	cfg.EncodeTime = zapcore.ISO8601TimeEncoder
 | |
| func NewProductionEncoderConfig() zapcore.EncoderConfig {
 | |
| 	return zapcore.EncoderConfig{
 | |
| 		TimeKey:        "ts",
 | |
| 		LevelKey:       "level",
 | |
| 		NameKey:        "logger",
 | |
| 		CallerKey:      "caller",
 | |
| 		FunctionKey:    zapcore.OmitKey,
 | |
| 		MessageKey:     "msg",
 | |
| 		StacktraceKey:  "stacktrace",
 | |
| 		LineEnding:     zapcore.DefaultLineEnding,
 | |
| 		EncodeLevel:    zapcore.LowercaseLevelEncoder,
 | |
| 		EncodeTime:     zapcore.EpochTimeEncoder,
 | |
| 		EncodeDuration: zapcore.SecondsDurationEncoder,
 | |
| 		EncodeCaller:   zapcore.ShortCallerEncoder,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewProductionConfig builds a reasonable default production logging
 | |
| // configuration.
 | |
| // Logging is enabled at InfoLevel and above, and uses a JSON encoder.
 | |
| // Logs are written to standard error.
 | |
| // Stacktraces are included on logs of ErrorLevel and above.
 | |
| // DPanicLevel logs will not panic, but will write a stacktrace.
 | |
| //
 | |
| // Sampling is enabled at 100:100 by default,
 | |
| // meaning that after the first 100 log entries
 | |
| // with the same level and message in the same second,
 | |
| // it will log every 100th entry
 | |
| // with the same level and message in the same second.
 | |
| // You may disable this behavior by setting Sampling to nil.
 | |
| //
 | |
| // See [NewProductionEncoderConfig] for information
 | |
| // on the default encoder configuration.
 | |
| func NewProductionConfig() Config {
 | |
| 	return Config{
 | |
| 		Level:       NewAtomicLevelAt(InfoLevel),
 | |
| 		Development: false,
 | |
| 		Sampling: &SamplingConfig{
 | |
| 			Initial:    100,
 | |
| 			Thereafter: 100,
 | |
| 		},
 | |
| 		Encoding:         "json",
 | |
| 		EncoderConfig:    NewProductionEncoderConfig(),
 | |
| 		OutputPaths:      []string{"stderr"},
 | |
| 		ErrorOutputPaths: []string{"stderr"},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for
 | |
| // development environments.
 | |
| //
 | |
| // Messages encoded with this configuration will use Zap's console encoder
 | |
| // intended to print human-readable output.
 | |
| // It will print log messages with the following information:
 | |
| //
 | |
| //   - The log level (e.g. "INFO", "ERROR").
 | |
| //   - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
 | |
| //   - The message passed to the log statement.
 | |
| //   - If available, a short path to the file and line number
 | |
| //     where the log statement was issued.
 | |
| //     The logger configuration determines whether this field is captured.
 | |
| //   - If available, a stacktrace from the line
 | |
| //     where the log statement was issued.
 | |
| //     The logger configuration determines whether this field is captured.
 | |
| //
 | |
| // By default, the following formats are used for different types:
 | |
| //
 | |
| //   - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z").
 | |
| //   - Duration is formatted as a string (e.g. "1.234s").
 | |
| //
 | |
| // You may change these by setting the appropriate fields in the returned
 | |
| // object.
 | |
| // For example, use the following to change the time encoding format:
 | |
| //
 | |
| //	cfg := zap.NewDevelopmentEncoderConfig()
 | |
| //	cfg.EncodeTime = zapcore.ISO8601TimeEncoder
 | |
| func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
 | |
| 	return zapcore.EncoderConfig{
 | |
| 		// Keys can be anything except the empty string.
 | |
| 		TimeKey:        "T",
 | |
| 		LevelKey:       "L",
 | |
| 		NameKey:        "N",
 | |
| 		CallerKey:      "C",
 | |
| 		FunctionKey:    zapcore.OmitKey,
 | |
| 		MessageKey:     "M",
 | |
| 		StacktraceKey:  "S",
 | |
| 		LineEnding:     zapcore.DefaultLineEnding,
 | |
| 		EncodeLevel:    zapcore.CapitalLevelEncoder,
 | |
| 		EncodeTime:     zapcore.ISO8601TimeEncoder,
 | |
| 		EncodeDuration: zapcore.StringDurationEncoder,
 | |
| 		EncodeCaller:   zapcore.ShortCallerEncoder,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewDevelopmentConfig builds a reasonable default development logging
 | |
| // configuration.
 | |
| // Logging is enabled at DebugLevel and above, and uses a console encoder.
 | |
| // Logs are written to standard error.
 | |
| // Stacktraces are included on logs of WarnLevel and above.
 | |
| // DPanicLevel logs will panic.
 | |
| //
 | |
| // See [NewDevelopmentEncoderConfig] for information
 | |
| // on the default encoder configuration.
 | |
| func NewDevelopmentConfig() Config {
 | |
| 	return Config{
 | |
| 		Level:            NewAtomicLevelAt(DebugLevel),
 | |
| 		Development:      true,
 | |
| 		Encoding:         "console",
 | |
| 		EncoderConfig:    NewDevelopmentEncoderConfig(),
 | |
| 		OutputPaths:      []string{"stderr"},
 | |
| 		ErrorOutputPaths: []string{"stderr"},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Build constructs a logger from the Config and Options.
 | |
| func (cfg Config) Build(opts ...Option) (*Logger, error) {
 | |
| 	enc, err := cfg.buildEncoder()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	sink, errSink, err := cfg.openSinks()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if cfg.Level == (AtomicLevel{}) {
 | |
| 		return nil, errors.New("missing Level")
 | |
| 	}
 | |
| 
 | |
| 	log := New(
 | |
| 		zapcore.NewCore(enc, sink, cfg.Level),
 | |
| 		cfg.buildOptions(errSink)...,
 | |
| 	)
 | |
| 	if len(opts) > 0 {
 | |
| 		log = log.WithOptions(opts...)
 | |
| 	}
 | |
| 	return log, nil
 | |
| }
 | |
| 
 | |
| func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
 | |
| 	opts := []Option{ErrorOutput(errSink)}
 | |
| 
 | |
| 	if cfg.Development {
 | |
| 		opts = append(opts, Development())
 | |
| 	}
 | |
| 
 | |
| 	if !cfg.DisableCaller {
 | |
| 		opts = append(opts, AddCaller())
 | |
| 	}
 | |
| 
 | |
| 	stackLevel := ErrorLevel
 | |
| 	if cfg.Development {
 | |
| 		stackLevel = WarnLevel
 | |
| 	}
 | |
| 	if !cfg.DisableStacktrace {
 | |
| 		opts = append(opts, AddStacktrace(stackLevel))
 | |
| 	}
 | |
| 
 | |
| 	if scfg := cfg.Sampling; scfg != nil {
 | |
| 		opts = append(opts, WrapCore(func(core zapcore.Core) zapcore.Core {
 | |
| 			var samplerOpts []zapcore.SamplerOption
 | |
| 			if scfg.Hook != nil {
 | |
| 				samplerOpts = append(samplerOpts, zapcore.SamplerHook(scfg.Hook))
 | |
| 			}
 | |
| 			return zapcore.NewSamplerWithOptions(
 | |
| 				core,
 | |
| 				time.Second,
 | |
| 				cfg.Sampling.Initial,
 | |
| 				cfg.Sampling.Thereafter,
 | |
| 				samplerOpts...,
 | |
| 			)
 | |
| 		}))
 | |
| 	}
 | |
| 
 | |
| 	if len(cfg.InitialFields) > 0 {
 | |
| 		fs := make([]Field, 0, len(cfg.InitialFields))
 | |
| 		keys := make([]string, 0, len(cfg.InitialFields))
 | |
| 		for k := range cfg.InitialFields {
 | |
| 			keys = append(keys, k)
 | |
| 		}
 | |
| 		sort.Strings(keys)
 | |
| 		for _, k := range keys {
 | |
| 			fs = append(fs, Any(k, cfg.InitialFields[k]))
 | |
| 		}
 | |
| 		opts = append(opts, Fields(fs...))
 | |
| 	}
 | |
| 
 | |
| 	return opts
 | |
| }
 | |
| 
 | |
| func (cfg Config) openSinks() (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
 | |
| 	sink, closeOut, err := Open(cfg.OutputPaths...)
 | |
| 	if err != nil {
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	errSink, _, err := Open(cfg.ErrorOutputPaths...)
 | |
| 	if err != nil {
 | |
| 		closeOut()
 | |
| 		return nil, nil, err
 | |
| 	}
 | |
| 	return sink, errSink, nil
 | |
| }
 | |
| 
 | |
| func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
 | |
| 	return newEncoder(cfg.Encoding, cfg.EncoderConfig)
 | |
| }
 |