github.com/avenga/couper@v1.12.2/handler/transport/connection.go (about) 1 package transport 2 3 import ( 4 "context" 5 "crypto/tls" 6 "net" 7 "sync" 8 "time" 9 10 "github.com/sirupsen/logrus" 11 "go.opentelemetry.io/otel/attribute" 12 "go.opentelemetry.io/otel/metric/instrument" 13 "go.opentelemetry.io/otel/metric/instrument/syncfloat64" 14 "go.opentelemetry.io/otel/metric/instrument/syncint64" 15 "go.opentelemetry.io/otel/metric/unit" 16 17 "github.com/avenga/couper/config/request" 18 "github.com/avenga/couper/telemetry/instrumentation" 19 "github.com/avenga/couper/telemetry/provider" 20 ) 21 22 const ( 23 eventOpen = "open" 24 eventClose = "close" 25 ) 26 27 // OriginConn wraps the original net.Conn created by net.DialContext or transport.DialTLS for debug purposes. 28 type OriginConn struct { 29 net.Conn 30 31 connClosedMu sync.Mutex 32 connClosed bool 33 34 conf *Config 35 36 createdAt time.Time 37 initialReqID string 38 labels []attribute.KeyValue 39 log *logrus.Entry 40 tlsState *tls.ConnectionState 41 } 42 43 // NewOriginConn creates a new wrapper with logging context. 44 func NewOriginConn(ctx context.Context, conn net.Conn, conf *Config, entry *logrus.Entry) *OriginConn { 45 var reqID string 46 if uid, ok := ctx.Value(request.UID).(string); ok { 47 reqID = uid 48 } 49 50 o := &OriginConn{ 51 Conn: conn, 52 conf: conf, 53 createdAt: time.Now(), 54 initialReqID: reqID, 55 labels: []attribute.KeyValue{ 56 attribute.String("origin", conf.Origin), 57 attribute.String("host", conf.Hostname), 58 attribute.String("backend", conf.BackendName), 59 }, 60 log: entry, 61 tlsState: nil, 62 } 63 64 if tlsConn, ok := conn.(*tls.Conn); ok { 65 state := tlsConn.ConnectionState() 66 o.tlsState = &state 67 } 68 entry.WithFields(o.logFields(eventOpen)).Debug() 69 70 counter, gauge := newMeterCounter() 71 72 counter.Add(ctx, 1, o.labels...) 73 gauge.Add(ctx, 1, o.labels...) 74 75 return o 76 } 77 78 func (o *OriginConn) logFields(event string) logrus.Fields { 79 fields := logrus.Fields{ 80 "event": event, 81 "initial_uid": o.initialReqID, 82 "localAddr": o.LocalAddr().String(), 83 "origin": o.conf.Origin, 84 "remoteAddr": o.RemoteAddr().String(), 85 } 86 87 if event == eventClose { 88 since := time.Since(o.createdAt) 89 90 meter := provider.Meter("couper/connection") 91 duration, _ := meter.SyncFloat64().Histogram( 92 instrumentation.BackendConnectionsLifetime, 93 instrument.WithDescription(string(unit.Dimensionless)), 94 ) 95 duration.Record(context.Background(), since.Seconds(), o.labels...) 96 97 fields["lifetime"] = since.Milliseconds() 98 } 99 100 return logrus.Fields{ 101 "connection": fields, 102 } 103 } 104 105 func (o *OriginConn) Close() error { 106 o.connClosedMu.Lock() 107 if o.connClosed { 108 o.connClosedMu.Unlock() 109 return nil 110 } 111 o.connClosed = true 112 o.connClosedMu.Unlock() 113 114 o.log.WithFields(o.logFields(eventClose)).Debug() 115 116 _, gauge := newMeterCounter() 117 gauge.Add(context.Background(), -1, o.labels...) 118 119 return o.Conn.Close() 120 } 121 122 func newMeterCounter() (syncint64.Counter, syncfloat64.UpDownCounter) { 123 meter := provider.Meter("couper/connection") 124 125 counter, _ := meter.SyncInt64(). 126 Counter(instrumentation.BackendConnectionsTotal, instrument.WithDescription(string(unit.Dimensionless))) 127 gauge, _ := meter.SyncFloat64().UpDownCounter( 128 instrumentation.BackendConnections, 129 instrument.WithDescription(string(unit.Dimensionless)), 130 ) 131 return counter, gauge 132 }