 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>
		
			
				
	
	
		
			438 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			14 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 (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"go.uber.org/zap/zapcore"
 | |
| 
 | |
| 	"go.uber.org/multierr"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	_oddNumberErrMsg    = "Ignored key without a value."
 | |
| 	_nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
 | |
| 	_multipleErrMsg     = "Multiple errors without a key."
 | |
| )
 | |
| 
 | |
| // A SugaredLogger wraps the base Logger functionality in a slower, but less
 | |
| // verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
 | |
| // method.
 | |
| //
 | |
| // Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
 | |
| // For each log level, it exposes four methods:
 | |
| //
 | |
| //   - methods named after the log level for log.Print-style logging
 | |
| //   - methods ending in "w" for loosely-typed structured logging
 | |
| //   - methods ending in "f" for log.Printf-style logging
 | |
| //   - methods ending in "ln" for log.Println-style logging
 | |
| //
 | |
| // For example, the methods for InfoLevel are:
 | |
| //
 | |
| //	Info(...any)           Print-style logging
 | |
| //	Infow(...any)          Structured logging (read as "info with")
 | |
| //	Infof(string, ...any)  Printf-style logging
 | |
| //	Infoln(...any)         Println-style logging
 | |
| type SugaredLogger struct {
 | |
| 	base *Logger
 | |
| }
 | |
| 
 | |
| // Desugar unwraps a SugaredLogger, exposing the original Logger. Desugaring
 | |
| // is quite inexpensive, so it's reasonable for a single application to use
 | |
| // both Loggers and SugaredLoggers, converting between them on the boundaries
 | |
| // of performance-sensitive code.
 | |
| func (s *SugaredLogger) Desugar() *Logger {
 | |
| 	base := s.base.clone()
 | |
| 	base.callerSkip -= 2
 | |
| 	return base
 | |
| }
 | |
| 
 | |
| // Named adds a sub-scope to the logger's name. See Logger.Named for details.
 | |
| func (s *SugaredLogger) Named(name string) *SugaredLogger {
 | |
| 	return &SugaredLogger{base: s.base.Named(name)}
 | |
| }
 | |
| 
 | |
| // WithOptions clones the current SugaredLogger, applies the supplied Options,
 | |
| // and returns the result. It's safe to use concurrently.
 | |
| func (s *SugaredLogger) WithOptions(opts ...Option) *SugaredLogger {
 | |
| 	base := s.base.clone()
 | |
| 	for _, opt := range opts {
 | |
| 		opt.apply(base)
 | |
| 	}
 | |
| 	return &SugaredLogger{base: base}
 | |
| }
 | |
| 
 | |
| // With adds a variadic number of fields to the logging context. It accepts a
 | |
| // mix of strongly-typed Field objects and loosely-typed key-value pairs. When
 | |
| // processing pairs, the first element of the pair is used as the field key
 | |
| // and the second as the field value.
 | |
| //
 | |
| // For example,
 | |
| //
 | |
| //	 sugaredLogger.With(
 | |
| //	   "hello", "world",
 | |
| //	   "failure", errors.New("oh no"),
 | |
| //	   Stack(),
 | |
| //	   "count", 42,
 | |
| //	   "user", User{Name: "alice"},
 | |
| //	)
 | |
| //
 | |
| // is the equivalent of
 | |
| //
 | |
| //	unsugared.With(
 | |
| //	  String("hello", "world"),
 | |
| //	  String("failure", "oh no"),
 | |
| //	  Stack(),
 | |
| //	  Int("count", 42),
 | |
| //	  Object("user", User{Name: "alice"}),
 | |
| //	)
 | |
| //
 | |
| // Note that the keys in key-value pairs should be strings. In development,
 | |
| // passing a non-string key panics. In production, the logger is more
 | |
| // forgiving: a separate error is logged, but the key-value pair is skipped
 | |
| // and execution continues. Passing an orphaned key triggers similar behavior:
 | |
| // panics in development and errors in production.
 | |
| func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
 | |
| 	return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)}
 | |
| }
 | |
| 
 | |
| // Level reports the minimum enabled level for this logger.
 | |
| //
 | |
| // For NopLoggers, this is [zapcore.InvalidLevel].
 | |
| func (s *SugaredLogger) Level() zapcore.Level {
 | |
| 	return zapcore.LevelOf(s.base.core)
 | |
| }
 | |
| 
 | |
| // Debug logs the provided arguments at [DebugLevel].
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Debug(args ...interface{}) {
 | |
| 	s.log(DebugLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Info logs the provided arguments at [InfoLevel].
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Info(args ...interface{}) {
 | |
| 	s.log(InfoLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Warn logs the provided arguments at [WarnLevel].
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Warn(args ...interface{}) {
 | |
| 	s.log(WarnLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Error logs the provided arguments at [ErrorLevel].
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Error(args ...interface{}) {
 | |
| 	s.log(ErrorLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // DPanic logs the provided arguments at [DPanicLevel].
 | |
| // In development, the logger then panics. (See [DPanicLevel] for details.)
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) DPanic(args ...interface{}) {
 | |
| 	s.log(DPanicLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Panic constructs a message with the provided arguments and panics.
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Panic(args ...interface{}) {
 | |
| 	s.log(PanicLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Fatal constructs a message with the provided arguments and calls os.Exit.
 | |
| // Spaces are added between arguments when neither is a string.
 | |
| func (s *SugaredLogger) Fatal(args ...interface{}) {
 | |
| 	s.log(FatalLevel, "", args, nil)
 | |
| }
 | |
| 
 | |
| // Debugf formats the message according to the format specifier
 | |
| // and logs it at [DebugLevel].
 | |
| func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
 | |
| 	s.log(DebugLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Infof formats the message according to the format specifier
 | |
| // and logs it at [InfoLevel].
 | |
| func (s *SugaredLogger) Infof(template string, args ...interface{}) {
 | |
| 	s.log(InfoLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Warnf formats the message according to the format specifier
 | |
| // and logs it at [WarnLevel].
 | |
| func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
 | |
| 	s.log(WarnLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Errorf formats the message according to the format specifier
 | |
| // and logs it at [ErrorLevel].
 | |
| func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
 | |
| 	s.log(ErrorLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // DPanicf formats the message according to the format specifier
 | |
| // and logs it at [DPanicLevel].
 | |
| // In development, the logger then panics. (See [DPanicLevel] for details.)
 | |
| func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
 | |
| 	s.log(DPanicLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Panicf formats the message according to the format specifier
 | |
| // and panics.
 | |
| func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
 | |
| 	s.log(PanicLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Fatalf formats the message according to the format specifier
 | |
| // and calls os.Exit.
 | |
| func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
 | |
| 	s.log(FatalLevel, template, args, nil)
 | |
| }
 | |
| 
 | |
| // Debugw logs a message with some additional context. The variadic key-value
 | |
| // pairs are treated as they are in With.
 | |
| //
 | |
| // When debug-level logging is disabled, this is much faster than
 | |
| //
 | |
| //	s.With(keysAndValues).Debug(msg)
 | |
| func (s *SugaredLogger) Debugw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(DebugLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Infow logs a message with some additional context. The variadic key-value
 | |
| // pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(InfoLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Warnw logs a message with some additional context. The variadic key-value
 | |
| // pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) Warnw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(WarnLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Errorw logs a message with some additional context. The variadic key-value
 | |
| // pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) Errorw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(ErrorLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // DPanicw logs a message with some additional context. In development, the
 | |
| // logger then panics. (See DPanicLevel for details.) The variadic key-value
 | |
| // pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) DPanicw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(DPanicLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Panicw logs a message with some additional context, then panics. The
 | |
| // variadic key-value pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) Panicw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(PanicLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Fatalw logs a message with some additional context, then calls os.Exit. The
 | |
| // variadic key-value pairs are treated as they are in With.
 | |
| func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
 | |
| 	s.log(FatalLevel, msg, nil, keysAndValues)
 | |
| }
 | |
| 
 | |
| // Debugln logs a message at [DebugLevel].
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Debugln(args ...interface{}) {
 | |
| 	s.logln(DebugLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Infoln logs a message at [InfoLevel].
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Infoln(args ...interface{}) {
 | |
| 	s.logln(InfoLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Warnln logs a message at [WarnLevel].
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Warnln(args ...interface{}) {
 | |
| 	s.logln(WarnLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Errorln logs a message at [ErrorLevel].
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Errorln(args ...interface{}) {
 | |
| 	s.logln(ErrorLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // DPanicln logs a message at [DPanicLevel].
 | |
| // In development, the logger then panics. (See [DPanicLevel] for details.)
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) DPanicln(args ...interface{}) {
 | |
| 	s.logln(DPanicLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Panicln logs a message at [PanicLevel] and panics.
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Panicln(args ...interface{}) {
 | |
| 	s.logln(PanicLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Fatalln logs a message at [FatalLevel] and calls os.Exit.
 | |
| // Spaces are always added between arguments.
 | |
| func (s *SugaredLogger) Fatalln(args ...interface{}) {
 | |
| 	s.logln(FatalLevel, args, nil)
 | |
| }
 | |
| 
 | |
| // Sync flushes any buffered log entries.
 | |
| func (s *SugaredLogger) Sync() error {
 | |
| 	return s.base.Sync()
 | |
| }
 | |
| 
 | |
| // log message with Sprint, Sprintf, or neither.
 | |
| func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
 | |
| 	// If logging at this level is completely disabled, skip the overhead of
 | |
| 	// string formatting.
 | |
| 	if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	msg := getMessage(template, fmtArgs)
 | |
| 	if ce := s.base.Check(lvl, msg); ce != nil {
 | |
| 		ce.Write(s.sweetenFields(context)...)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // logln message with Sprintln
 | |
| func (s *SugaredLogger) logln(lvl zapcore.Level, fmtArgs []interface{}, context []interface{}) {
 | |
| 	if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	msg := getMessageln(fmtArgs)
 | |
| 	if ce := s.base.Check(lvl, msg); ce != nil {
 | |
| 		ce.Write(s.sweetenFields(context)...)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // getMessage format with Sprint, Sprintf, or neither.
 | |
| func getMessage(template string, fmtArgs []interface{}) string {
 | |
| 	if len(fmtArgs) == 0 {
 | |
| 		return template
 | |
| 	}
 | |
| 
 | |
| 	if template != "" {
 | |
| 		return fmt.Sprintf(template, fmtArgs...)
 | |
| 	}
 | |
| 
 | |
| 	if len(fmtArgs) == 1 {
 | |
| 		if str, ok := fmtArgs[0].(string); ok {
 | |
| 			return str
 | |
| 		}
 | |
| 	}
 | |
| 	return fmt.Sprint(fmtArgs...)
 | |
| }
 | |
| 
 | |
| // getMessageln format with Sprintln.
 | |
| func getMessageln(fmtArgs []interface{}) string {
 | |
| 	msg := fmt.Sprintln(fmtArgs...)
 | |
| 	return msg[:len(msg)-1]
 | |
| }
 | |
| 
 | |
| func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
 | |
| 	if len(args) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		// Allocate enough space for the worst case; if users pass only structured
 | |
| 		// fields, we shouldn't penalize them with extra allocations.
 | |
| 		fields    = make([]Field, 0, len(args))
 | |
| 		invalid   invalidPairs
 | |
| 		seenError bool
 | |
| 	)
 | |
| 
 | |
| 	for i := 0; i < len(args); {
 | |
| 		// This is a strongly-typed field. Consume it and move on.
 | |
| 		if f, ok := args[i].(Field); ok {
 | |
| 			fields = append(fields, f)
 | |
| 			i++
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// If it is an error, consume it and move on.
 | |
| 		if err, ok := args[i].(error); ok {
 | |
| 			if !seenError {
 | |
| 				seenError = true
 | |
| 				fields = append(fields, Error(err))
 | |
| 			} else {
 | |
| 				s.base.Error(_multipleErrMsg, Error(err))
 | |
| 			}
 | |
| 			i++
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Make sure this element isn't a dangling key.
 | |
| 		if i == len(args)-1 {
 | |
| 			s.base.Error(_oddNumberErrMsg, Any("ignored", args[i]))
 | |
| 			break
 | |
| 		}
 | |
| 
 | |
| 		// Consume this value and the next, treating them as a key-value pair. If the
 | |
| 		// key isn't a string, add this pair to the slice of invalid pairs.
 | |
| 		key, val := args[i], args[i+1]
 | |
| 		if keyStr, ok := key.(string); !ok {
 | |
| 			// Subsequent errors are likely, so allocate once up front.
 | |
| 			if cap(invalid) == 0 {
 | |
| 				invalid = make(invalidPairs, 0, len(args)/2)
 | |
| 			}
 | |
| 			invalid = append(invalid, invalidPair{i, key, val})
 | |
| 		} else {
 | |
| 			fields = append(fields, Any(keyStr, val))
 | |
| 		}
 | |
| 		i += 2
 | |
| 	}
 | |
| 
 | |
| 	// If we encountered any invalid key-value pairs, log an error.
 | |
| 	if len(invalid) > 0 {
 | |
| 		s.base.Error(_nonStringKeyErrMsg, Array("invalid", invalid))
 | |
| 	}
 | |
| 	return fields
 | |
| }
 | |
| 
 | |
| type invalidPair struct {
 | |
| 	position   int
 | |
| 	key, value interface{}
 | |
| }
 | |
| 
 | |
| func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
 | |
| 	enc.AddInt64("position", int64(p.position))
 | |
| 	Any("key", p.key).AddTo(enc)
 | |
| 	Any("value", p.value).AddTo(enc)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| type invalidPairs []invalidPair
 | |
| 
 | |
| func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
 | |
| 	var err error
 | |
| 	for i := range ps {
 | |
| 		err = multierr.Append(err, enc.AppendObject(ps[i]))
 | |
| 	}
 | |
| 	return err
 | |
| }
 |