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  }