joao-r-reis commented on code in PR #1755:
URL: 
https://github.com/apache/cassandra-gocql-driver/pull/1755#discussion_r2143349809


##########
logger.go:
##########
@@ -28,33 +28,346 @@ import (
        "bytes"
        "fmt"
        "log"
+       "net"
+       "strconv"
+       "strings"
+       "sync"
 )
 
-type StdLogger interface {
-       Print(v ...interface{})
-       Printf(format string, v ...interface{})
-       Println(v ...interface{})
-}
+// Deprecated: use StructuredLogger instead
+type StdLogger interface{}
 
 type nopLogger struct{}
 
-func (n nopLogger) Print(_ ...interface{}) {}
+func (n nopLogger) Error(_ string, _ ...LogField) {}
+
+func (n nopLogger) Warning(_ string, _ ...LogField) {}
+
+func (n nopLogger) Info(_ string, _ ...LogField) {}
 
-func (n nopLogger) Printf(_ string, _ ...interface{}) {}
+func (n nopLogger) Debug(_ string, _ ...LogField) {}
 
-func (n nopLogger) Println(_ ...interface{}) {}
+var nopLoggerSingleton = &nopLogger{}
 
 type testLogger struct {
-       capture bytes.Buffer
+       logLevel LogLevel
+       capture  bytes.Buffer
+       mu       sync.Mutex
+}
+
+func newTestLogger(logLevel LogLevel) *testLogger {
+       return &testLogger{logLevel: logLevel}
+}
+
+func (l *testLogger) Error(msg string, fields ...LogField) {
+       if LogLevelError <= l.logLevel {
+               l.write("ERR gocql: ", msg, fields)
+       }
+}
+
+func (l *testLogger) Warning(msg string, fields ...LogField) {
+       if LogLevelWarn <= l.logLevel {
+               l.write("WRN gocql: ", msg, fields)
+       }
+}
+
+func (l *testLogger) Info(msg string, fields ...LogField) {
+       if LogLevelInfo <= l.logLevel {
+               l.write("INF gocql: ", msg, fields)
+       }
+}
+
+func (l *testLogger) Debug(msg string, fields ...LogField) {
+       if LogLevelDebug <= l.logLevel {
+               l.write("DBG gocql: ", msg, fields)
+       }
+}
+
+func (l *testLogger) write(prefix string, msg string, fields []LogField) {
+       buf := bytes.Buffer{}
+       writeLogMsg(&buf, prefix, msg, fields)
+       l.mu.Lock()
+       defer l.mu.Unlock()
+       l.capture.WriteString(buf.String() + "\n")
+}
+
+func (l *testLogger) String() string {
+       l.mu.Lock()
+       defer l.mu.Unlock()
+       return l.capture.String()
+}
+
+type defaultLogger struct {
+       logLevel LogLevel
+}
+
+// NewLogger creates an StructuredLogger that uses the standard library "log" 
package.
+func NewLogger(logLevel LogLevel) StructuredLogger {
+       return &defaultLogger{logLevel: logLevel}
+}
+
+func (l *defaultLogger) Error(msg string, fields ...LogField) {
+       if LogLevelError <= l.logLevel {
+               l.write("ERR gocql: ", msg, fields)
+       }
+}
+
+func (l *defaultLogger) Warning(msg string, fields ...LogField) {
+       if LogLevelWarn <= l.logLevel {
+               l.write("WRN gocql: ", msg, fields)
+       }
+}
+
+func (l *defaultLogger) Info(msg string, fields ...LogField) {
+       if LogLevelInfo <= l.logLevel {
+               l.write("INF gocql: ", msg, fields)
+       }
+}
+
+func (l *defaultLogger) Debug(msg string, fields ...LogField) {
+       if LogLevelDebug <= l.logLevel {
+               l.write("DBG gocql: ", msg, fields)
+       }
+}
+
+func (l *defaultLogger) write(prefix string, msg string, fields []LogField) {
+       buf := bytes.Buffer{}
+       writeLogMsg(&buf, prefix, msg, fields)
+       log.Println(buf.String())
+}
+
+func writeFields(buf *bytes.Buffer, fields []LogField) {
+       for i, field := range fields {
+               if i > 0 {
+                       buf.WriteRune(' ')
+               }
+               buf.WriteString(field.Name)
+               buf.WriteRune('=')
+               buf.WriteString(field.Value.String())
+       }
+}
+
+func writeLogMsg(buf *bytes.Buffer, prefix string, msg string, fields 
[]LogField) {
+       buf.WriteString(prefix)
+       buf.WriteString(msg)
+       buf.WriteRune(' ')
+       writeFields(buf, fields)
+}
+
+type LogLevel int
+
+const (
+       LogLevelDebug = LogLevel(5)
+       LogLevelInfo  = LogLevel(4)
+       LogLevelWarn  = LogLevel(3)
+       LogLevelError = LogLevel(2)
+       LogLevelNone  = LogLevel(0)
+)
+
+func (recv LogLevel) String() string {
+       switch recv {
+       case LogLevelDebug:
+               return "debug"
+       case LogLevelInfo:
+               return "info"
+       case LogLevelWarn:
+               return "warn"
+       case LogLevelError:
+               return "error"
+       case LogLevelNone:
+               return "none"
+       default:
+               // fmt.sprintf allocates so use strings.Join instead
+               temp := [2]string{"invalid level ", strconv.Itoa(int(recv))}
+               return strings.Join(temp[:], "")
+       }
+}
+
+type LogField struct {
+       Name  string
+       Value LogFieldValue
+}
+
+func newLogField(name string, value LogFieldValue) LogField {
+       return LogField{
+               Name:  name,
+               Value: value,
+       }
 }
 
-func (l *testLogger) Print(v ...interface{})                 { 
fmt.Fprint(&l.capture, v...) }
-func (l *testLogger) Printf(format string, v ...interface{}) { 
fmt.Fprintf(&l.capture, format, v...) }
-func (l *testLogger) Println(v ...interface{})               { 
fmt.Fprintln(&l.capture, v...) }
-func (l *testLogger) String() string                         { return 
l.capture.String() }
+func newLogFieldIp(name string, value net.IP) LogField {
+       var str string
+       if value != nil {
+               str = value.String()
+       }
+       return newLogField(name, logFieldValueString(str))
+}
+
+func newLogFieldError(name string, value error) LogField {
+       var str string
+       if value != nil {
+               str = value.Error()
+       }
+       return newLogField(name, logFieldValueString(str))
+}
+
+func newLogFieldStringer(name string, value fmt.Stringer) LogField {
+       var str string
+       if value != nil {
+               str = value.String()
+       }
+       return newLogField(name, logFieldValueString(str))
+}
+
+func newLogFieldString(name string, value string) LogField {
+       return newLogField(name, logFieldValueString(value))
+}
+
+func newLogFieldInt(name string, value int) LogField {
+       return newLogField(name, logFieldValueInt64(int64(value)))
+}
+
+func newLogFieldBool(name string, value bool) LogField {
+       return newLogField(name, logFieldValueBool(value))
+}
+
+type StructuredLogger interface {
+       Error(msg string, fields ...LogField)
+       Warning(msg string, fields ...LogField)
+       Info(msg string, fields ...LogField)
+       Debug(msg string, fields ...LogField)
+}
+
+// A LogFieldValue can represent any Go value, but unlike type any,
+// it can represent most small values without an allocation.
+// The zero Value corresponds to nil.
+type LogFieldValue struct {
+       num uint64
+       any interface{}
+}
+
+// LogFieldValueType is the type of a [LogFieldValue].
+type LogFieldValueType int
+
+// It's important that LogFieldTypeAny is 0 so that a zero Value represents 
nil.
+const (
+       LogFieldTypeAny LogFieldValueType = iota
+       LogFieldTypeBool
+       LogFieldTypeInt64
+       LogFieldTypeString
+)
+
+// LogFieldValueType returns v's LogFieldValueType.
+func (v LogFieldValue) LogFieldValueType() LogFieldValueType {
+       switch x := v.any.(type) {
+       case LogFieldValueType:
+               return x
+       case string:
+               return LogFieldTypeString
+       default:
+               return LogFieldTypeAny
+       }
+}
+
+func logFieldValueString(value string) LogFieldValue {
+       return LogFieldValue{num: uint64(len(value)), any: value}
+}
 
-type defaultLogger struct{}
+func logFieldValueInt(v int) LogFieldValue {
+       return logFieldValueInt64(int64(v))
+}
+
+func logFieldValueInt64(v int64) LogFieldValue {
+       return LogFieldValue{num: uint64(v), any: LogFieldTypeInt64}
+}
+
+func logFieldValueBool(v bool) LogFieldValue {
+       u := uint64(0)
+       if v {
+               u = 1
+       }
+       return LogFieldValue{num: u, any: LogFieldTypeBool}
+}
+
+// Any returns v's value as an interface.
+func (v LogFieldValue) Any() interface{} {
+       switch v.LogFieldValueType() {
+       case LogFieldTypeAny:
+               if k, ok := v.any.(LogFieldValueType); ok {
+                       return k
+               }
+               return v.any
+       case LogFieldTypeInt64:
+               return int64(v.num)
+       case LogFieldTypeString:
+               return v.str()
+       case LogFieldTypeBool:
+               return v.bool()
+       default:
+               panic(fmt.Sprintf("bad value type: %s", v.LogFieldValueType()))
+       }
+}
+
+// String returns Value's value as a string, formatted like [fmt.Sprint]. 
Unlike
+// the methods Int64, Float64, and so on, which panic if v is of the
+// wrong kind, String never panics.

Review Comment:
   That is true but there should be no way for a user to create an object with 
an invalid type. The `never panics` reference here is referencing the fact that 
other methods to retrieve the underlying value panic if the value is not of 
that specific type but in `String()` case it can be called for any type, not 
just values of type `LogFieldTypeString`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscr...@cassandra.apache.org
For additional commands, e-mail: pr-h...@cassandra.apache.org

Reply via email to