Repository: prebid/prebid-server
Pull request #1343 (2020-06-08 06:20:56)

Add Outgoing Connection Metrics
Add metrics to track the number of connections per host and differentiate between an actively used connection vs an idle connection.

Technical Reference:
https://medium.com/@deeeet/trancing-http-request-latency-in-golang-65b2463f548c
https://blog.golang.org/http-tracing


guscarreon: Below is the `diff` command output in prometheus metrics after a post request to bidder `appnexus`

**A new connection and connection wait time**
```
168c168
< prebid_server_adapter_connection_created{adapter="appnexus"} 0
---
> prebid_server_adapter_connection_created{adapter="appnexus"} 1
375a376,390
> # HELP prebid_server_adapter_connection_wait Seconds from when the connection was requested until it is either created or reused
> # TYPE prebid_server_adapter_connection_wait histogram
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.05"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.1"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.15"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.2"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.25"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.3"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.4"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.5"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.75"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="1"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="+Inf"} 1
> prebid_server_adapter_connection_wait_sum{adapter="appnexus"} 0.03102841
> prebid_server_adapter_connection_wait_count{adapter="appnexus"} 1
602c617
```

**DNS lookup metrics**
```
< prebid_server_connections_closed 0
---
> prebid_server_connections_closed 1
4562c4577
< prebid_server_connections_opened 0
---
> prebid_server_connections_opened 1
4568,4580c4583,4595
< prebid_server_dns_lookup_time_bucket{le="0.05"} 0
< prebid_server_dns_lookup_time_bucket{le="0.1"} 0
< prebid_server_dns_lookup_time_bucket{le="0.15"} 0
< prebid_server_dns_lookup_time_bucket{le="0.2"} 0
< prebid_server_dns_lookup_time_bucket{le="0.25"} 0
< prebid_server_dns_lookup_time_bucket{le="0.3"} 0
< prebid_server_dns_lookup_time_bucket{le="0.4"} 0
< prebid_server_dns_lookup_time_bucket{le="0.5"} 0
< prebid_server_dns_lookup_time_bucket{le="0.75"} 0
< prebid_server_dns_lookup_time_bucket{le="1"} 0
< prebid_server_dns_lookup_time_bucket{le="+Inf"} 0
< prebid_server_dns_lookup_time_sum 0
< prebid_server_dns_lookup_time_count 0
---
> prebid_server_dns_lookup_time_bucket{le="0.05"} 1
> prebid_server_dns_lookup_time_bucket{le="0.1"} 1
> prebid_server_dns_lookup_time_bucket{le="0.15"} 1
> prebid_server_dns_lookup_time_bucket{le="0.2"} 1
> prebid_server_dns_lookup_time_bucket{le="0.25"} 1
> prebid_server_dns_lookup_time_bucket{le="0.3"} 1
> prebid_server_dns_lookup_time_bucket{le="0.4"} 1
> prebid_server_dns_lookup_time_bucket{le="0.5"} 1
> prebid_server_dns_lookup_time_bucket{le="0.75"} 1
> prebid_server_dns_lookup_time_bucket{le="1"} 1
> prebid_server_dns_lookup_time_bucket{le="+Inf"} 1
> prebid_server_dns_lookup_time_sum 0.022126272
> prebid_server_dns_lookup_time_count 1
4587c4602
```
guscarreon: Modified according to feedback. Kept both the error connection metrics and the connection idle time. Added a configuration boolean flag to disable the adapter connection metrics. 

Ran the server locally and posted a request to check out if the metrics work as expected. These are the metrics before and after said request to the appnexus bidder.

```
170c170
< prebid_server_adapter_connection_created{adapter="appnexus"} 0
---
> prebid_server_adapter_connection_created{adapter="appnexus"} 1
578,590c578,590
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.05"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.1"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.15"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.2"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.25"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.3"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.4"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.5"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.75"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="1"} 0
< prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="+Inf"} 0
< prebid_server_adapter_connection_wait_sum{adapter="appnexus"} 0
< prebid_server_adapter_connection_wait_count{adapter="appnexus"} 0
---
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.05"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.1"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.15"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.2"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.25"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.3"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.4"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.5"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="0.75"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="1"} 1
> prebid_server_adapter_connection_wait_bucket{adapter="appnexus",le="+Inf"} 1
> prebid_server_adapter_connection_wait_sum{adapter="appnexus"} 0.036550788
> prebid_server_adapter_connection_wait_count{adapter="appnexus"} 1
    .
    .
    .
5581c5581
< prebid_server_connections_closed 0
---
> prebid_server_connections_closed 1
5588c5588
< prebid_server_connections_opened 0
---
> prebid_server_connections_opened 1
5594,5606c5594,5606
< prebid_server_dns_lookup_time_bucket{le="0.05"} 0
< prebid_server_dns_lookup_time_bucket{le="0.1"} 0
< prebid_server_dns_lookup_time_bucket{le="0.15"} 0
< prebid_server_dns_lookup_time_bucket{le="0.2"} 0
< prebid_server_dns_lookup_time_bucket{le="0.25"} 0
< prebid_server_dns_lookup_time_bucket{le="0.3"} 0
< prebid_server_dns_lookup_time_bucket{le="0.4"} 0
< prebid_server_dns_lookup_time_bucket{le="0.5"} 0
< prebid_server_dns_lookup_time_bucket{le="0.75"} 0
< prebid_server_dns_lookup_time_bucket{le="1"} 0
< prebid_server_dns_lookup_time_bucket{le="+Inf"} 0
< prebid_server_dns_lookup_time_sum 0
< prebid_server_dns_lookup_time_count 0
---
> prebid_server_dns_lookup_time_bucket{le="0.05"} 1
> prebid_server_dns_lookup_time_bucket{le="0.1"} 1
> prebid_server_dns_lookup_time_bucket{le="0.15"} 1
> prebid_server_dns_lookup_time_bucket{le="0.2"} 1
> prebid_server_dns_lookup_time_bucket{le="0.25"} 1
> prebid_server_dns_lookup_time_bucket{le="0.3"} 1
> prebid_server_dns_lookup_time_bucket{le="0.4"} 1
> prebid_server_dns_lookup_time_bucket{le="0.5"} 1
> prebid_server_dns_lookup_time_bucket{le="0.75"} 1
> prebid_server_dns_lookup_time_bucket{le="1"} 1
> prebid_server_dns_lookup_time_bucket{le="+Inf"} 1
> prebid_server_dns_lookup_time_sum 0.022831942
> prebid_server_dns_lookup_time_count 1
```

Patch:
diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go
index 4b1215de..054fa7f6 100644
--- a/adapters/adgeneration/adgeneration.go
+++ b/adapters/adgeneration/adgeneration.go
@@ -210,6 +210,7 @@ func (adg *AdgenerationAdapter) MakeBids(internalRequest *openrtb.BidRequest, ex
 				Bid:     &bid,
 				BidType: bitType,
 			})
+			bidResponse.Currency = adg.getCurrency(internalRequest)
 			return bidResponse, nil
 		}
 	}
@@ -254,7 +255,7 @@ func removeWrapper(ad string) string {
 func NewAdgenerationAdapter(endpoint string) *AdgenerationAdapter {
 	return &AdgenerationAdapter{
 		endpoint,
-		"1.0.0",
+		"1.0.1",
 		"JPY",
 	}
 }
diff --git a/adapters/audienceNetwork/facebook.go b/adapters/audienceNetwork/facebook.go
index f4091e4e..d9f6719f 100644
--- a/adapters/audienceNetwork/facebook.go
+++ b/adapters/audienceNetwork/facebook.go
@@ -54,6 +54,12 @@ func (this *FacebookAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *
 		}}
 	}
 
+	if request.Site != nil {
+		return nil, []error{&errortypes.BadInput{
+			Message: "Site impressions are not supported.",
+		}}
+	}
+
 	return this.buildRequests(request)
 }
 
@@ -143,10 +149,6 @@ func (this *FacebookAdapter) modifyRequest(out *openrtb.BidRequest) error {
 		app := *out.App
 		app.Publisher = &openrtb.Publisher{ID: pubId}
 		out.App = &app
-	} else {
-		site := *out.Site
-		site.Publisher = &openrtb.Publisher{ID: pubId}
-		out.Site = &site
 	}
 
 	if err = this.modifyImp(imp); err != nil {
@@ -468,15 +470,11 @@ func (fa *FacebookAdapter) MakeTimeoutNotification(req *adapters.RequestData) (*
 		return &adapters.RequestData{}, []error{err}
 	}
 
-	// The publisher ID is either in the app object or the site object, depending on the supply of the request so we need
-	// to check both
+	// The publisher ID is expected in the app object
 	pubID, err = jsonparser.GetString(req.Body, "app", "publisher", "id")
 	if err != nil {
-		pubID, err = jsonparser.GetString(req.Body, "site", "publisher", "id")
-		if err != nil {
-			return &adapters.RequestData{}, []error{
-				errors.New("path [app|site].publisher.id not found in the request"),
-			}
+		return &adapters.RequestData{}, []error{
+			errors.New("path app.publisher.id not found in the request"),
 		}
 	}
 
diff --git a/config/config.go b/config/config.go
index 2e7f875b..a82dbb5e 100755
--- a/config/config.go
+++ b/config/config.go
@@ -379,6 +379,11 @@ type Metrics struct {
 type DisabledMetrics struct {
 	// True if we want to stop collecting account-to-adapter metrics
 	AccountAdapterDetails bool `mapstructure:"account_adapter_details"`
+
+	// True if we don't want to collect metrics about the connections prebid
+	// server establishes with bidder servers such as the number of connections
+	// that were created or reused.
+	AdapterConnectionMetrics bool `mapstructure:"adapter_connections_metrics"`
 }
 
 func (cfg *Metrics) validate(errs configErrors) configErrors {
@@ -688,6 +693,7 @@ func SetupViper(v *viper.Viper, filename string) {
 	v.SetDefault("http_client_cache.idle_connection_timeout_seconds", 60)
 	// no metrics configured by default (metrics{host|database|username|password})
 	v.SetDefault("metrics.disabled_metrics.account_adapter_details", false)
+	v.SetDefault("metrics.disabled_metrics.adapter_connections_metrics", true)
 	v.SetDefault("metrics.influxdb.host", "")
 	v.SetDefault("metrics.influxdb.database", "")
 	v.SetDefault("metrics.influxdb.username", "")
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index e8b5d3ec..8efba5a9 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -388,6 +388,14 @@ func (deps *endpointDeps) overrideWithParams(httpRequest *http.Request, req *ope
 
 	setAmpExt(req.Site, "1")
 
+	account := httpRequest.FormValue("account")
+	if account != "" {
+		if req.Site.Publisher == nil {
+			req.Site.Publisher = &openrtb.Publisher{}
+		}
+		req.Site.Publisher.ID = account
+	}
+
 	slot := httpRequest.FormValue("slot")
 	if slot != "" {
 		req.Imp[0].TagID = slot
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index 53607ac5..2ecddb83 100755
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -201,7 +201,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
 	for name, bidder := range ortbBidders {
 		// Clean out any disabled bidders
 		if infos[string(name)].Status == adapters.StatusActive {
-			allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client, cfg, me)
+			allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client, cfg, me, name)
 		}
 	}
 
diff --git a/exchange/bidder.go b/exchange/bidder.go
index ee6a4942..7c39b72b 100644
--- a/exchange/bidder.go
+++ b/exchange/bidder.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/http/httptrace"
 	"time"
 
 	"github.com/golang/glog"
@@ -87,20 +88,30 @@ type pbsOrtbSeatBid struct {
 //
 // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter"
 // (which is being phased out and replaced by Bidder for OpenRTB auctions)
-func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me pbsmetrics.MetricsEngine) adaptedBidder {
+func adaptBidder(bidder adapters.Bidder, client *http.Client, cfg *config.Configuration, me pbsmetrics.MetricsEngine, name openrtb_ext.BidderName) adaptedBidder {
 	return &bidderAdapter{
-		Bidder:      bidder,
-		Client:      client,
-		DebugConfig: cfg.Debug,
-		me:          me,
+		Bidder:     bidder,
+		BidderName: name,
+		Client:     client,
+		me:         me,
+		config: bidderAdapterConfig{
+			Debug:              cfg.Debug,
+			DisableConnMetrics: cfg.Metrics.Disabled.AdapterConnectionMetrics,
+		},
 	}
 }
 
 type bidderAdapter struct {
-	Bidder      adapters.Bidder
-	Client      *http.Client
-	DebugConfig config.Debug
-	me          pbsmetrics.MetricsEngine
+	Bidder     adapters.Bidder
+	BidderName openrtb_ext.BidderName
+	Client     *http.Client
+	me         pbsmetrics.MetricsEngine
+	config     bidderAdapterConfig
+}
+
+type bidderAdapterConfig struct {
+	Debug              config.Debug
+	DisableConnMetrics bool
 }
 
 func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currencies.Conversions, reqInfo *adapters.ExtraRequestInfo) (*pbsOrtbSeatBid, []error) {
@@ -325,6 +336,11 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re
 	}
 	httpReq.Header = req.Headers
 
+	// If adapter connection metrics are not disabled, add the client trace
+	// to get complete connection info into our metrics
+	if !bidder.config.DisableConnMetrics {
+		ctx = bidder.addClientTrace(ctx)
+	}
 	httpResp, err := ctxhttp.Do(ctx, bidder.Client, httpReq)
 	if err != nil {
 		if err == context.DeadlineExceeded {
@@ -387,7 +403,7 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou
 			httpResp, err := ctxhttp.Do(ctx, bidder.Client, httpReq)
 			success := (err == nil && httpResp.StatusCode >= 200 && httpResp.StatusCode < 300)
 			bidder.me.RecordTimeoutNotice(success)
-			if bidder.DebugConfig.TimeoutNotification.Log && !(bidder.DebugConfig.TimeoutNotification.FailOnly && success) {
+			if bidder.config.Debug.TimeoutNotification.Log && !(bidder.config.Debug.TimeoutNotification.FailOnly && success) {
 				var msg string
 				if err == nil {
 					msg = fmt.Sprintf("TimeoutNotification: status:(%d) body:%s", httpResp.StatusCode, string(toReq.Body))
@@ -395,16 +411,16 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou
 					msg = fmt.Sprintf("TimeoutNotification: error:(%s) body:%s", err.Error(), string(toReq.Body))
 				}
 				// If logging is turned on, and logging is not disallowed via FailOnly
-				util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate)
+				util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate)
 			}
 		} else {
 			bidder.me.RecordTimeoutNotice(false)
-			if bidder.DebugConfig.TimeoutNotification.Log {
+			if bidder.config.Debug.TimeoutNotification.Log {
 				msg := fmt.Sprintf("TimeoutNotification: Failed to make timeout request: method(%s), uri(%s), error(%s)", toReq.Method, toReq.Uri, err.Error())
-				util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate)
+				util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate)
 			}
 		}
-	} else if bidder.DebugConfig.TimeoutNotification.Log {
+	} else if bidder.config.Debug.TimeoutNotification.Log {
 		reqJSON, err := json.Marshal(req)
 		var msg string
 		if err == nil {
@@ -412,7 +428,7 @@ func (bidder *bidderAdapter) doTimeoutNotification(timeoutBidder adapters.Timeou
 		} else {
 			msg = fmt.Sprintf("TimeoutNotification: Failed to generate timeout request: error(%s), bidder request marshal failed(%s)", errL[0].Error(), err.Error())
 		}
-		util.LogRandomSample(msg, logger, bidder.DebugConfig.TimeoutNotification.SamplingRate)
+		util.LogRandomSample(msg, logger, bidder.config.Debug.TimeoutNotification.SamplingRate)
 	}
 
 }
@@ -422,3 +438,34 @@ type httpCallInfo struct {
 	response *adapters.ResponseData
 	err      error
 }
+
+// This function adds an httptrace.ClientTrace object to the context so, if connection with the bidder
+// endpoint is established, we can keep track of whether the connection was newly created, reused, and
+// the time from the connection request, to the connection creation.
+func (bidder *bidderAdapter) addClientTrace(ctx context.Context) context.Context {
+	var connStart, dnsStart time.Time
+
+	trace := &httptrace.ClientTrace{
+		// GetConn is called before a connection is created or retrieved from an idle pool
+		GetConn: func(hostPort string) {
+			connStart = time.Now()
+		},
+		// GotConn is called after a successful connection is obtained
+		GotConn: func(info httptrace.GotConnInfo) {
+			connWaitTime := time.Now().Sub(connStart)
+
+			bidder.me.RecordAdapterConnections(bidder.BidderName, info.Reused, connWaitTime)
+		},
+		// DNSStart is called when a DNS lookup begins.
+		DNSStart: func(info httptrace.DNSStartInfo) {
+			dnsStart = time.Now()
+		},
+		// DNSDone is called when a DNS lookup ends.
+		DNSDone: func(info httptrace.DNSDoneInfo) {
+			dnsLookupTime := time.Now().Sub(dnsStart)
+
+			bidder.me.RecordDNSTime(dnsLookupTime)
+		},
+	}
+	return httptrace.WithClientTrace(ctx, trace)
+}
diff --git a/go.mod b/go.mod
index 72bb9b74..00cadd31 100644
--- a/go.mod
+++ b/go.mod
@@ -22,6 +22,7 @@ require (
 	github.com/influxdata/influxdb v1.6.1 // indirect
 	github.com/julienschmidt/httprouter v1.1.0
 	github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
+	github.com/kr/pretty v0.2.0 // indirect
 	github.com/lib/pq v1.0.0
 	github.com/magiconair/properties v1.8.0
 	github.com/mattn/go-colorable v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index 35b2b765..5eaf37ca 100644
--- a/go.sum
+++ b/go.sum
@@ -45,6 +45,11 @@ github.com/julienschmidt/httprouter v1.1.0 h1:7wLdtIiIpzOkC9u6sXOozpBauPdskj3ru4
 github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
diff --git a/pbsmetrics/config/metrics.go b/pbsmetrics/config/metrics.go
index 0dbe9a69..6a36f9e7 100644
--- a/pbsmetrics/config/metrics.go
+++ b/pbsmetrics/config/metrics.go
@@ -37,7 +37,7 @@ func NewMetricsEngine(cfg *config.Configuration, adapterList []openrtb_ext.Bidde
 	}
 	if cfg.Metrics.Prometheus.Port != 0 {
 		// Set up the Prometheus metrics.
-		returnEngine.PrometheusMetrics = prometheusmetrics.NewMetrics(cfg.Metrics.Prometheus)
+		returnEngine.PrometheusMetrics = prometheusmetrics.NewMetrics(cfg.Metrics.Prometheus, cfg.Metrics.Disabled)
 		engineList = append(engineList, returnEngine.PrometheusMetrics)
 	}
 
@@ -118,6 +118,21 @@ func (me *MultiMetricsEngine) RecordAdapterRequest(labels pbsmetrics.AdapterLabe
 	}
 }
 
+// Keeps track of created and reused connections to adapter bidders and the time from the
+// connection request, to the connection creation, or reuse from the pool across all engines
+func (me *MultiMetricsEngine) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) {
+	for _, thisME := range *me {
+		thisME.RecordAdapterConnections(bidderName, connWasReused, connWaitTime)
+	}
+}
+
+// Times the DNS resolution process
+func (me *MultiMetricsEngine) RecordDNSTime(dnsLookupTime time.Duration) {
+	for _, thisME := range *me {
+		thisME.RecordDNSTime(dnsLookupTime)
+	}
+}
+
 // RecordAdapterBidReceived across all engines
 func (me *MultiMetricsEngine) RecordAdapterBidReceived(labels pbsmetrics.AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) {
 	for _, thisME := range *me {
@@ -237,6 +252,14 @@ func (me *DummyMetricsEngine) RecordAdapterPanic(labels pbsmetrics.AdapterLabels
 func (me *DummyMetricsEngine) RecordAdapterRequest(labels pbsmetrics.AdapterLabels) {
 }
 
+// RecordAdapterConnections as a noop
+func (me *DummyMetricsEngine) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) {
+}
+
+// RecordDNSTime as a noop
+func (me *DummyMetricsEngine) RecordDNSTime(dnsLookupTime time.Duration) {
+}
+
 // RecordAdapterBidReceived as a noop
 func (me *DummyMetricsEngine) RecordAdapterBidReceived(labels pbsmetrics.AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) {
 }
diff --git a/pbsmetrics/go_metrics.go b/pbsmetrics/go_metrics.go
index 836434bf..26f6ce07 100644
--- a/pbsmetrics/go_metrics.go
+++ b/pbsmetrics/go_metrics.go
@@ -29,6 +29,7 @@ type Metrics struct {
 	PrebidCacheRequestTimerError   metrics.Timer
 	StoredReqCacheMeter            map[CacheResult]metrics.Meter
 	StoredImpCacheMeter            map[CacheResult]metrics.Meter
+	DNSLookupTimer                 metrics.Timer
 
 	// Metrics for OpenRTB requests specifically. So we can track what % of RequestsMeter are OpenRTB
 	// and know when legacy requests have been abandoned.
@@ -81,6 +82,9 @@ type AdapterMetrics struct {
 	BidsReceivedMeter metrics.Meter
 	PanicMeter        metrics.Meter
 	MarkupMetrics     map[openrtb_ext.BidType]*MarkupDeliveryMetrics
+	ConnCreated       metrics.Counter
+	ConnReused        metrics.Counter
+	ConnWaitTime      metrics.Timer
 }
 
 type MarkupDeliveryMetrics struct {
@@ -106,7 +110,7 @@ const unknownBidder openrtb_ext.BidderName = "unknown"
 // rather than loading legacy metrics that never get filled.
 // This will also eventually let us configure metrics, such as setting a limited set of metrics
 // for a production instance, and then expanding again when we need more debugging.
-func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disableMetrics config.DisabledMetrics) *Metrics {
+func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disabledMetrics config.DisabledMetrics) *Metrics {
 	blankMeter := &metrics.NilMeter{}
 	blankTimer := &metrics.NilTimer{}
 
@@ -123,6 +127,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa
 		SafariRequestMeter:             blankMeter,
 		SafariNoCookieMeter:            blankMeter,
 		RequestTimer:                   blankTimer,
+		DNSLookupTimer:                 blankTimer,
 		RequestsQueueTimer:             make(map[RequestType]map[bool]metrics.Timer),
 		PrebidCacheRequestTimerSuccess: blankTimer,
 		PrebidCacheRequestTimerError:   blankTimer,
@@ -153,13 +158,13 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa
 
 		AdapterMetrics:  make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)),
 		accountMetrics:  make(map[string]*accountMetrics),
-		MetricsDisabled: disableMetrics,
+		MetricsDisabled: disabledMetrics,
 
 		exchanges: exchanges,
 	}
 
 	for _, a := range exchanges {
-		newMetrics.AdapterMetrics[a] = makeBlankAdapterMetrics()
+		newMetrics.AdapterMetrics[a] = makeBlankAdapterMetrics(newMetrics.MetricsDisabled)
 	}
 
 	for _, t := range RequestTypes() {
@@ -209,6 +214,7 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d
 	newMetrics.AppRequestMeter = metrics.GetOrRegisterMeter("app_requests", registry)
 	newMetrics.SafariNoCookieMeter = metrics.GetOrRegisterMeter("safari_no_cookie_requests", registry)
 	newMetrics.RequestTimer = metrics.GetOrRegisterTimer("request_time", registry)
+	newMetrics.DNSLookupTimer = metrics.GetOrRegisterTimer("dns_lookup_time", registry)
 	newMetrics.PrebidCacheRequestTimerSuccess = metrics.GetOrRegisterTimer("prebid_cache_request_time.ok", registry)
 	newMetrics.PrebidCacheRequestTimerError = metrics.GetOrRegisterTimer("prebid_cache_request_time.err", registry)
 
@@ -255,7 +261,7 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d
 }
 
 // Part of setting up blank metrics, the adapter metrics.
-func makeBlankAdapterMetrics() *AdapterMetrics {
+func makeBlankAdapterMetrics(disabledMetrics config.DisabledMetrics) *AdapterMetrics {
 	blankMeter := &metrics.NilMeter{}
 	newAdapter := &AdapterMetrics{
 		NoCookieMeter:     blankMeter,
@@ -268,6 +274,11 @@ func makeBlankAdapterMetrics() *AdapterMetrics {
 		PanicMeter:        blankMeter,
 		MarkupMetrics:     makeBlankBidMarkupMetrics(),
 	}
+	if !disabledMetrics.AdapterConnectionMetrics {
+		newAdapter.ConnCreated = metrics.NilCounter{}
+		newAdapter.ConnReused = metrics.NilCounter{}
+		newAdapter.ConnWaitTime = &metrics.NilTimer{}
+	}
 	for _, err := range AdapterErrors() {
 		newAdapter.ErrorMeters[err] = blankMeter
 	}
@@ -302,6 +313,9 @@ func registerAdapterMetrics(registry metrics.Registry, adapterOrAccount string,
 		openrtb_ext.BidTypeAudio:  makeDeliveryMetrics(registry, adapterOrAccount+"."+exchange, openrtb_ext.BidTypeAudio),
 		openrtb_ext.BidTypeNative: makeDeliveryMetrics(registry, adapterOrAccount+"."+exchange, openrtb_ext.BidTypeNative),
 	}
+	am.ConnCreated = metrics.GetOrRegisterCounter(fmt.Sprintf("%[1]s.%[2]s.connections_created", adapterOrAccount, exchange), registry)
+	am.ConnReused = metrics.GetOrRegisterCounter(fmt.Sprintf("%[1]s.%[2]s.connections_reused", adapterOrAccount, exchange), registry)
+	am.ConnWaitTime = metrics.GetOrRegisterTimer(fmt.Sprintf("%[1]s.%[2]s.connection_wait_time", adapterOrAccount, exchange), registry)
 	for err := range am.ErrorMeters {
 		am.ErrorMeters[err] = metrics.GetOrRegisterMeter(fmt.Sprintf("%s.%s.requests.%s", adapterOrAccount, exchange, err), registry)
 	}
@@ -348,7 +362,7 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics {
 	am.adapterMetrics = make(map[openrtb_ext.BidderName]*AdapterMetrics, len(me.exchanges))
 	if !me.MetricsDisabled.AccountAdapterDetails {
 		for _, a := range me.exchanges {
-			am.adapterMetrics[a] = makeBlankAdapterMetrics()
+			am.adapterMetrics[a] = makeBlankAdapterMetrics(me.MetricsDisabled)
 			registerAdapterMetrics(me.MetricsRegistry, fmt.Sprintf("account.%s", id), string(a), am.adapterMetrics[a])
 		}
 	}
@@ -472,6 +486,34 @@ func (me *Metrics) RecordAdapterRequest(labels AdapterLabels) {
 	}
 }
 
+// Keeps track of created and reused connections to adapter bidders and the time from the
+// connection request, to the connection creation, or reuse from the pool across all engines
+func (me *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName,
+	connWasReused bool,
+	connWaitTime time.Duration) {
+
+	if me.MetricsDisabled.AdapterConnectionMetrics {
+		return
+	}
+
+	am, ok := me.AdapterMetrics[adapterName]
+	if !ok {
+		glog.Errorf("Trying to log adapter connection metrics for %s: adapter not found", string(adapterName))
+		return
+	}
+
+	if connWasReused {
+		am.ConnReused.Inc(1)
+	} else {
+		am.ConnCreated.Inc(1)
+	}
+	am.ConnWaitTime.Update(connWaitTime)
+}
+
+func (me *Metrics) RecordDNSTime(dnsLookupTime time.Duration) {
+	me.DNSLookupTimer.Update(dnsLookupTime)
+}
+
 // RecordAdapterBidReceived implements a part of the MetricsEngine interface.
 // This tracks how many bids from each Bidder use `adm` vs. `nurl.
 func (me *Metrics) RecordAdapterBidReceived(labels AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) {
diff --git a/pbsmetrics/metrics.go b/pbsmetrics/metrics.go
index 514fbac1..8133bc73 100644
--- a/pbsmetrics/metrics.go
+++ b/pbsmetrics/metrics.go
@@ -301,6 +301,8 @@ type MetricsEngine interface {
 	RecordLegacyImps(labels Labels, numImps int)           // RecordImps for the legacy engine
 	RecordRequestTime(labels Labels, length time.Duration) // ignores adapter. only statusOk and statusErr fom status
 	RecordAdapterRequest(labels AdapterLabels)
+	RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration)
+	RecordDNSTime(dnsLookupTime time.Duration)
 	RecordAdapterPanic(labels AdapterLabels)
 	// This records whether or not a bid of a particular type uses `adm` or `nurl`.
 	// Since the legacy endpoints don't have a bid type, it can only count bids from OpenRTB and AMP.
diff --git a/pbsmetrics/metrics_mock.go b/pbsmetrics/metrics_mock.go
index 6c263f0a..42a2d1b4 100644
--- a/pbsmetrics/metrics_mock.go
+++ b/pbsmetrics/metrics_mock.go
@@ -52,6 +52,16 @@ func (me *MetricsEngineMock) RecordAdapterRequest(labels AdapterLabels) {
 	me.Called(labels)
 }
 
+// RecordAdapterConnections mock
+func (me *MetricsEngineMock) RecordAdapterConnections(bidderName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) {
+	me.Called(bidderName, connWasReused, connWaitTime)
+}
+
+// RecordDNSTime mock
+func (me *MetricsEngineMock) RecordDNSTime(dnsLookupTime time.Duration) {
+	me.Called(dnsLookupTime)
+}
+
 // RecordAdapterBidReceived mock
 func (me *MetricsEngineMock) RecordAdapterBidReceived(labels AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) {
 	me.Called(labels, bidType, hasAdm)
diff --git a/pbsmetrics/prometheus/preload.go b/pbsmetrics/prometheus/preload.go
index ef1d300c..4f62a18a 100644
--- a/pbsmetrics/prometheus/preload.go
+++ b/pbsmetrics/prometheus/preload.go
@@ -85,6 +85,20 @@ func preloadLabelValues(m *Metrics) {
 		hasBidsLabel: boolValues,
 	})
 
+	if !m.metricsDisabled.AdapterConnectionMetrics {
+		preloadLabelValuesForCounter(m.adapterCreatedConnections, map[string][]string{
+			adapterLabel: adapterValues,
+		})
+
+		preloadLabelValuesForCounter(m.adapterReusedConnections, map[string][]string{
+			adapterLabel: adapterValues,
+		})
+
+		preloadLabelValuesForHistogram(m.adapterConnectionWaitTime, map[string][]string{
+			adapterLabel: adapterValues,
+		})
+	}
+
 	preloadLabelValuesForHistogram(m.adapterRequestsTimer, map[string][]string{
 		adapterLabel: adapterValues,
 	})
diff --git a/pbsmetrics/prometheus/prometheus.go b/pbsmetrics/prometheus/prometheus.go
index d94c4d78..b42399b2 100644
--- a/pbsmetrics/prometheus/prometheus.go
+++ b/pbsmetrics/prometheus/prometheus.go
@@ -29,23 +29,29 @@ type Metrics struct {
 	storedImpressionsCacheResult *prometheus.CounterVec
 	storedRequestCacheResult     *prometheus.CounterVec
 	timeoutNotifications         *prometheus.CounterVec
+	dnsLookupTimer               prometheus.Histogram
 	privacyCCPA                  *prometheus.CounterVec
 	privacyCOPPA                 *prometheus.CounterVec
 	privacyLMT                   *prometheus.CounterVec
 	privacyTCF                   *prometheus.CounterVec
 
 	// Adapter Metrics
-	adapterBids          *prometheus.CounterVec
-	adapterCookieSync    *prometheus.CounterVec
-	adapterErrors        *prometheus.CounterVec
-	adapterPanics        *prometheus.CounterVec
-	adapterPrices        *prometheus.HistogramVec
-	adapterRequests      *prometheus.CounterVec
-	adapterRequestsTimer *prometheus.HistogramVec
-	adapterUserSync      *prometheus.CounterVec
+	adapterBids               *prometheus.CounterVec
+	adapterCookieSync         *prometheus.CounterVec
+	adapterErrors             *prometheus.CounterVec
+	adapterPanics             *prometheus.CounterVec
+	adapterPrices             *prometheus.HistogramVec
+	adapterRequests           *prometheus.CounterVec
+	adapterRequestsTimer      *prometheus.HistogramVec
+	adapterUserSync           *prometheus.CounterVec
+	adapterReusedConnections  *prometheus.CounterVec
+	adapterCreatedConnections *prometheus.CounterVec
+	adapterConnectionWaitTime *prometheus.HistogramVec
 
 	// Account Metrics
 	accountRequests *prometheus.CounterVec
+
+	metricsDisabled config.DisabledMetrics
 }
 
 const (
@@ -97,14 +103,15 @@ const (
 )
 
 // NewMetrics initializes a new Prometheus metrics instance with preloaded label values.
-func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
-	requestTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1}
+func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMetrics) *Metrics {
+	standardTimeBuckets := []float64{0.05, 0.1, 0.15, 0.20, 0.25, 0.3, 0.4, 0.5, 0.75, 1}
 	cacheWriteTimeBuckets := []float64{0.001, 0.002, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1}
 	priceBuckets := []float64{250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000}
 	queuedRequestTimeBuckets := []float64{0, 1, 5, 30, 60, 120, 180, 240, 300}
 
 	metrics := Metrics{}
 	metrics.Registry = prometheus.NewRegistry()
+	metrics.metricsDisabled = disabledMetrics
 
 	metrics.connectionsClosed = newCounterWithoutLabels(cfg, metrics.Registry,
 		"connections_closed",
@@ -132,7 +139,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"impressions_requests_legacy",
 		"Count of requested impressions to Prebid Server using the legacy endpoint.")
 
-	metrics.prebidCacheWriteTimer = newHistogram(cfg, metrics.Registry,
+	metrics.prebidCacheWriteTimer = newHistogramVec(cfg, metrics.Registry,
 		"prebidcache_write_time_seconds",
 		"Seconds to write to Prebid Cache labeled by success or failure. Failure timing is limited by Prebid Server enforced timeouts.",
 		[]string{successLabel},
@@ -143,11 +150,11 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"Count of total requests to Prebid Server labeled by type and status.",
 		[]string{requestTypeLabel, requestStatusLabel})
 
-	metrics.requestsTimer = newHistogram(cfg, metrics.Registry,
+	metrics.requestsTimer = newHistogramVec(cfg, metrics.Registry,
 		"request_time_seconds",
 		"Seconds to resolve successful Prebid Server requests labeled by type.",
 		[]string{requestTypeLabel},
-		requestTimeBuckets)
+		standardTimeBuckets)
 
 	metrics.requestsWithoutCookie = newCounter(cfg, metrics.Registry,
 		"requests_without_cookie",
@@ -169,6 +176,11 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"Count of timeout notifications triggered, and if they were successfully sent.",
 		[]string{successLabel})
 
+	metrics.dnsLookupTimer = newHistogram(cfg, metrics.Registry,
+		"dns_lookup_time",
+		"Seconds to resolve DNS",
+		standardTimeBuckets)
+
 	metrics.privacyCCPA = newCounter(cfg, metrics.Registry,
 		"privacy_ccpa",
 		"Count of total requests to Prebid Server where CCPA was provided by source and opt-out .",
@@ -209,7 +221,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"Count of panics labeled by adapter.",
 		[]string{adapterLabel})
 
-	metrics.adapterPrices = newHistogram(cfg, metrics.Registry,
+	metrics.adapterPrices = newHistogramVec(cfg, metrics.Registry,
 		"adapter_prices",
 		"Monetary value of the bids labeled by adapter.",
 		[]string{adapterLabel},
@@ -220,11 +232,29 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"Count of requests labeled by adapter, if has a cookie, and if it resulted in bids.",
 		[]string{adapterLabel, cookieLabel, hasBidsLabel})
 
-	metrics.adapterRequestsTimer = newHistogram(cfg, metrics.Registry,
+	if !metrics.metricsDisabled.AdapterConnectionMetrics {
+		metrics.adapterCreatedConnections = newCounter(cfg, metrics.Registry,
+			"adapter_connection_created",
+			"Count that keeps track of new connections when contacting adapter bidder endpoints.",
+			[]string{adapterLabel})
+
+		metrics.adapterReusedConnections = newCounter(cfg, metrics.Registry,
+			"adapter_connection_reused",
+			"Count that keeps track of reused connections when contacting adapter bidder endpoints.",
+			[]string{adapterLabel})
+
+		metrics.adapterConnectionWaitTime = newHistogramVec(cfg, metrics.Registry,
+			"adapter_connection_wait",
+			"Seconds from when the connection was requested until it is either created or reused",
+			[]string{adapterLabel},
+			standardTimeBuckets)
+	}
+
+	metrics.adapterRequestsTimer = newHistogramVec(cfg, metrics.Registry,
 		"adapter_request_time_seconds",
 		"Seconds to resolve each successful request labeled by adapter.",
 		[]string{adapterLabel},
-		requestTimeBuckets)
+		standardTimeBuckets)
 
 	metrics.adapterUserSync = newCounter(cfg, metrics.Registry,
 		"adapter_user_sync",
@@ -236,7 +266,7 @@ func NewMetrics(cfg config.PrometheusMetrics) *Metrics {
 		"Count of total requests to Prebid Server labeled by account.",
 		[]string{accountLabel})
 
-	metrics.requestsQueueTimer = newHistogram(cfg, metrics.Registry,
+	metrics.requestsQueueTimer = newHistogramVec(cfg, metrics.Registry,
 		"request_queue_time",
 		"Seconds request was waiting in queue",
 		[]string{requestTypeLabel, requestStatusLabel},
@@ -271,7 +301,7 @@ func newCounterWithoutLabels(cfg config.PrometheusMetrics, registry *prometheus.
 	return counter
 }
 
-func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec {
+func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec {
 	opts := prometheus.HistogramOpts{
 		Namespace: cfg.Namespace,
 		Subsystem: cfg.Subsystem,
@@ -284,6 +314,19 @@ func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, n
 	return histogram
 }
 
+func newHistogram(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, buckets []float64) prometheus.Histogram {
+	opts := prometheus.HistogramOpts{
+		Namespace: cfg.Namespace,
+		Subsystem: cfg.Subsystem,
+		Name:      name,
+		Help:      help,
+		Buckets:   buckets,
+	}
+	histogram := prometheus.NewHistogram(opts)
+	registry.MustRegister(histogram)
+	return histogram
+}
+
 func (m *Metrics) RecordConnectionAccept(success bool) {
 	if success {
 		m.connectionsOpened.Inc()
@@ -359,6 +402,32 @@ func (m *Metrics) RecordAdapterRequest(labels pbsmetrics.AdapterLabels) {
 	}
 }
 
+// Keeps track of created and reused connections to adapter bidders and the time from the
+// connection request, to the connection creation, or reuse from the pool across all engines
+func (m *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) {
+	if m.metricsDisabled.AdapterConnectionMetrics {
+		return
+	}
+
+	if connWasReused {
+		m.adapterReusedConnections.With(prometheus.Labels{
+			adapterLabel: string(adapterName),
+		}).Inc()
+	} else {
+		m.adapterCreatedConnections.With(prometheus.Labels{
+			adapterLabel: string(adapterName),
+		}).Inc()
+	}
+
+	m.adapterConnectionWaitTime.With(prometheus.Labels{
+		adapterLabel: string(adapterName),
+	}).Observe(connWaitTime.Seconds())
+}
+
+func (m *Metrics) RecordDNSTime(dnsLookupTime time.Duration) {
+	m.dnsLookupTimer.Observe(dnsLookupTime.Seconds())
+}
+
 func (m *Metrics) RecordAdapterPanic(labels pbsmetrics.AdapterLabels) {
 	m.adapterPanics.With(prometheus.Labels{
 		adapterLabel: string(labels.Adapter),
diff --git a/static/bidder-info/audienceNetwork.yaml b/static/bidder-info/audienceNetwork.yaml
index 56230bf3..324e5c6d 100644
--- a/static/bidder-info/audienceNetwork.yaml
+++ b/static/bidder-info/audienceNetwork.yaml
@@ -1,11 +1,6 @@
 maintainer:
   email: "none"
 capabilities:
-  site:
-    mediaTypes:
-      - banner
-      - video
-      - native
   app:
     mediaTypes:
       - banner
diff --git a/static/category-mapping/freewheel/freewheel.json b/static/category-mapping/freewheel/freewheel.json
index 1c4a4fa2..11529206 100644
--- a/static/category-mapping/freewheel/freewheel.json
+++ b/static/category-mapping/freewheel/freewheel.json
@@ -1178,5 +1178,81 @@
 	"IAB22-3": {
 		"id": "410",
 		"name": "Product"
+	},  
+	"IAB1": {
+	  "id": "392",
+	  "name": "Entertainment"
+	},
+	"IAB2": {
+	  "id": "399",
+	  "name": "Automotive"
+	},
+	"IAB3": {
+	  "id": "393",
+	  "name": "Business Services"
+	},
+	"IAB4": {
+	  "id": "405",
+	  "name": "Educational Services"
+	},
+	"IAB5": {
+	  "id": "405",
+	  "name": "Educational Services"
+	},
+	"IAB7": {
+	  "id": "406",
+	  "name": "Health Care Services"
+	},
+	"IAB8": {
+	  "id": "394",
+	  "name": "Food"
+	},
+	"IAB9": {
+	  "id": "392",
+	  "name": "Entertainment"
+	},
+	"IAB10": {
+	  "id": "434",
+	  "name": "Home Furnishings"
+	},
+	"IAB11": {
+	  "id": "398",
+	  "name": "Government/Municipal"
+	},
+	"IAB12": {
+	  "id": "438",
+	  "name": "News"
+	},
+	"IAB13": {
+	  "id": "393",
+	  "name": "Business Services"
+	},
+	"IAB16": {
+	  "id": "423",
+	  "name": "Pet Food/Supplies"
+	},
+	"IAB17": {
+	  "id": "425",
+	  "name": "Professional Sports"
+	},
+	"IAB18": {
+	  "id": "397",
+	  "name": "Apparel"
+	},
+	"IAB19": {
+	  "id": "409",
+	  "name": "Computing Product"
+	},
+	"IAB20": {
+	  "id": "395",
+	  "name": "Travel/Hotel/Airlines"
+	},
+	"IAB21": {
+	  "id": "416",
+	  "name": "Real Estate"
+	},
+	"IAB22": {
+	  "id": "403",
+	  "name": "Retail Stores/Chains"
 	}
 }
\ No newline at end of file


Test patch:
diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go
index e76995fc..3c795ea2 100644
--- a/adapters/adgeneration/adgeneration_test.go
+++ b/adapters/adgeneration/adgeneration_test.go
@@ -5,7 +5,9 @@ import (
 	"testing"
 
 	"github.com/mxmCherry/openrtb"
+	"github.com/prebid/prebid-server/adapters"
 	"github.com/prebid/prebid-server/adapters/adapterstest"
+	"github.com/stretchr/testify/assert"
 )
 
 func TestJsonSamples(t *testing.T) {
@@ -174,3 +176,64 @@ func TestCreateAd(t *testing.T) {
 		t.Errorf("%v does not match createAd.", adgVastResponse)
 	}
 }
+
+func TestMakeBids(t *testing.T) {
+	bidder := NewAdgenerationAdapter("https://d.socdm.com/adsv/v1")
+	internalRequest := &openrtb.BidRequest{
+		ID: "test-success-bid-request",
+		Imp: []openrtb.Imp{
+			{ID: "bidRequest-success-test", Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}}}, Ext: json.RawMessage(`{"bidder": { "id": "58278" }}`)},
+		},
+		Device: &openrtb.Device{UA: "testUA", IP: "testIP"},
+		Site:   &openrtb.Site{Page: "https://supership.com"},
+		User:   &openrtb.User{BuyerUID: "buyerID"},
+	}
+	externalRequest := adapters.RequestData{}
+	response := adapters.ResponseData{
+		StatusCode: 200,
+		Body:       ([]byte)("{\n \"ad\": \"testAd\",\n \"cpm\": 30,\n \"creativeid\": \"Dummy_supership.jp\",\n \"h\": 250,\n \"locationid\": \"58278\",\n \"results\": [{}],\n \"dealid\": \"test-deal-id\",\n \"w\": 300\n }"),
+	}
+	// default Currency InternalRequest
+	defaultCurBidderResponse, errs := bidder.MakeBids(internalRequest, &externalRequest, &response)
+	if len(errs) > 0 {
+		t.Errorf("MakeBids return errors. errors: %v", errs)
+	}
+	checkBidResponse(t, defaultCurBidderResponse, bidder.defaultCurrency)
+
+	// Specified Currency InternalRequest
+	usdCur := "USD"
+	internalRequest.Cur = []string{usdCur}
+	specifiedCurBidderResponse, errs := bidder.MakeBids(internalRequest, &externalRequest, &response)
+	if len(errs) > 0 {
+		t.Errorf("MakeBids return errors. errors: %v", errs)
+	}
+	checkBidResponse(t, specifiedCurBidderResponse, usdCur)
+
+}
+
+func checkBidResponse(t *testing.T, bidderResponse *adapters.BidderResponse, expectedCurrency string) {
+	if bidderResponse == nil {
+		t.Errorf("actual bidResponse is nil.")
+	}
+
+	// AdM is assured by TestCreateAd and JSON tests
+	var expectedAdM string = "testAd"
+	var expectedID string = "58278"
+	var expectedImpID = "bidRequest-success-test"
+	var expectedPrice float64 = 30.0
+	var expectedW uint64 = 300
+	var expectedH uint64 = 250
+	var expectedCrID string = "Dummy_supership.jp"
+	var extectedDealID string = "test-deal-id"
+
+	assert.Equal(t, expectedCurrency, bidderResponse.Currency)
+	assert.Equal(t, 1, len(bidderResponse.Bids))
+	assert.Equal(t, expectedID, bidderResponse.Bids[0].Bid.ID)
+	assert.Equal(t, expectedImpID, bidderResponse.Bids[0].Bid.ImpID)
+	assert.Equal(t, expectedAdM, bidderResponse.Bids[0].Bid.AdM)
+	assert.Equal(t, expectedPrice, bidderResponse.Bids[0].Bid.Price)
+	assert.Equal(t, expectedW, bidderResponse.Bids[0].Bid.W)
+	assert.Equal(t, expectedH, bidderResponse.Bids[0].Bid.H)
+	assert.Equal(t, expectedCrID, bidderResponse.Bids[0].Bid.CrID)
+	assert.Equal(t, extectedDealID, bidderResponse.Bids[0].Bid.DealID)
+}
diff --git a/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json
index d23a510b..10bf1c4a 100644
--- a/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json
+++ b/adapters/adgeneration/adgenerationtest/exemplary/single-banner.json
@@ -52,7 +52,7 @@
                 "tmax": 500
             },
             "expectedRequest":{
-                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
+                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
                 "headers": {
                     "Accept": [
                         "application/json"
diff --git a/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json
index cf8635bb..bc469a1e 100644
--- a/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json
+++ b/adapters/adgeneration/adgenerationtest/supplemental/204-bid-response.json
@@ -52,7 +52,7 @@
                 "tmax": 500
             },
             "expectedRequest":{
-                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
+                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
                 "headers": {
                     "Accept": [
                         "application/json"
diff --git a/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json
index f5dc7fe0..6ac92d9a 100644
--- a/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json
+++ b/adapters/adgeneration/adgenerationtest/supplemental/400-bid-response.json
@@ -52,7 +52,7 @@
                 "tmax": 500
             },
             "expectedRequest":{
-                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
+                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
                 "headers": {
                     "Accept": [
                         "application/json"
diff --git a/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json b/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json
index 399f85a5..a0abb66d 100644
--- a/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json
+++ b/adapters/adgeneration/adgenerationtest/supplemental/no-bid-response.json
@@ -52,7 +52,7 @@
                 "tmax": 500
             },
             "expectedRequest":{
-                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.0&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
+                "uri": "https://d.socdm.com/adsv/v1?adapterver=1.0.1&currency=JPY&hb=true&id=58278&posall=SSPLOC&sdkname=prebidserver&sdktype=0&size=300%C3%97250&t=json3&tp=http%3A%2F%2Fexample.com%2Ftest.html",
                 "headers": {
                     "Accept": [
                         "application/json"
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json
deleted file mode 100644
index 01bab3df..00000000
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/banner-site.json
+++ /dev/null
@@ -1,132 +0,0 @@
-{
-    "mockBidRequest": {
-        "id": "test-req-id",
-        "imp": [
-            {
-                "id": "test-imp-id",
-                "banner": {
-                    "format": [
-                        {
-                            "w": 300,
-                            "h": 250
-                        }
-                    ],
-                    "w": 300,
-                    "h": 250
-                },
-                "ext": {
-                    "bidder": {
-                        "publisherid": "123",
-                        "placementid": "456"
-                    }
-                }
-            }
-        ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
-        },
-        "device": {
-            "ip": "152.193.6.74"
-        },
-        "user": {
-            "id": "db089de9-a62e-4861-a881-0ff15e052516",
-            "buyeruid": "v4_bidder_token"
-        },
-        "tmax": 500
-    },
-    "httpcalls": [
-        {
-            "expectedRequest": {
-                "uri": "https://an.facebook.com/placementbid.ortb",
-                "headers": {
-                    "Accept": [
-                        "application/json"
-                    ],
-                    "Content-Type": [
-                        "application/json;charset=utf-8"
-                    ],
-                    "X-Fb-Pool-Routing-Token": [
-                        "v4_bidder_token"
-                    ]
-                },
-                "body": {
-                    "id": "test-imp-id",
-                    "imp": [
-                        {
-                            "id": "test-imp-id",
-                            "banner": {
-                                "w": -1,
-                                "h": 250
-                            },
-                            "tagid": "123_456"
-                        }
-                    ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
-                        "publisher": {
-                            "id": "123"
-                        }
-                    },
-                    "device": {
-                        "ip": "152.193.6.74"
-                    },
-                    "user": {
-                        "id": "db089de9-a62e-4861-a881-0ff15e052516",
-                        "buyeruid": "v4_bidder_token"
-                    },
-                    "tmax": 500,
-                    "ext": {
-                        "authentication_id": "4e24a2b23fbfb5e41a9093b921d6cddf497c24dd5f63879038cec2ab2f27d174",
-                        "platformid": "test-platform-id"
-                    }
-                }
-            },
-            "mockResponse": {
-                "status": 200,
-                "body": {
-                    "id": "test-imp-id",
-                    "seatbid": [
-                        {
-                            "bid": [
-                                {
-                                    "id": "987",
-                                    "impid": "test-imp-id",
-                                    "price": 1.000000,
-                                    "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}",
-                                    "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0",
-                                    "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0",
-                                    "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}"
-                                }
-                            ]
-                        }
-                    ],
-                    "bidid": "654",
-                    "cur": "USD"
-                }
-            }
-        }
-    ],
-    "expectedBidResponses": [
-        {
-            "currency": "USD",
-            "bids": [
-                {
-                    "bid": {
-                        "id": "987",
-                        "impid": "test-imp-id",
-                        "price": 1,
-                        "adm": "{\"type\":\"ID\",\"bid_id\":\"987\",\"placement_id\":\"123_456\",\"resolved_placement_id\":\"123_456\",\"sdk_version\":\"5.5.0\",\"device_id\":\"abc\",\"template\":1,\"payload\":null,\"bid_time_token\":\"v4_bidder_token=\"}",
-                        "adid": "987",
-                        "crid": "987",
-                        "nurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=0&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0",
-                        "lurl": "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&ortb_loss_code=${AUCTION_LOSS}&clearing_price=${AUCTION_PRICE}&app_version=iOS-1.0",
-                        "burl": "https://www.facebook.com/audiencenetwork/burl/?partner=test-platform-id&app=def&placement=456&auction=123&impression=123&request=123478&bid=987&clearing_price=${AUCTION_PRICE}"
-                    },
-                    "type": "banner"
-                }
-            ]
-        }
-    ]
-}
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
index 9f563f11..573032c8 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/interstitial.json
@@ -23,9 +23,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -64,9 +64,9 @@
                             "tagid": "123_456"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "123"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
index 16bed344..08639bee 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/native-1.1.json
@@ -16,9 +16,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -56,9 +56,9 @@
                             "tagid": "123_456"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "123"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
index 5ece0f08..35bdf9a4 100644
--- a/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
+++ b/adapters/audienceNetwork/audienceNetworktest/exemplary/video.json
@@ -21,9 +21,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -66,9 +66,9 @@
                             "tagid": "123_456"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "123"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json
index 5469fefb..450e0d9e 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/banner-format-only.json
@@ -24,9 +24,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -64,9 +64,9 @@
                             "tagid": "123_456"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "123"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json
index f145f5fe..c33807bd 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-adm.json
@@ -18,9 +18,9 @@
                 }
             }
         }],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -55,9 +55,9 @@
                     },
                     "tagid": "123_456"
                 }],
-                "site": {
-                    "domain": "prebid.org",
-                    "page": "prebid.org",
+                "app": {
+                    "id": "app-abc",
+                    "bundle": "com.prebid",
                     "publisher": {
                         "id": "123"
                     }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json
index fa9fd913..b229d41a 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-banner-height.json
@@ -22,9 +22,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json
index ad19d94c..68ca8044 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/invalid-interstitial.json
@@ -20,9 +20,9 @@
                 }
             }
         }],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json
index b57c9001..50212155 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm-bidid.json
@@ -18,9 +18,9 @@
                 }
             }
         }],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -55,9 +55,9 @@
                     },
                     "tagid": "123_456"
                 }],
-                "site": {
-                    "domain": "prebid.org",
-                    "page": "prebid.org",
+                "app": {
+                    "id": "app-abc",
+                    "bundle": "com.prebid",
                     "publisher": {
                         "id": "123"
                     }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json
index 23227aab..832b16dc 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-adm.json
@@ -18,9 +18,9 @@
                 }
             }
         }],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -55,9 +55,9 @@
                     },
                     "tagid": "123_456"
                 }],
-                "site": {
-                    "domain": "prebid.org",
-                    "page": "prebid.org",
+                "app": {
+                    "id": "app-abc",
+                    "bundle": "com.prebid",
                     "publisher": {
                         "id": "123"
                     }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json
index 016e8de0..0793f990 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/missing-banner-height.json
@@ -20,9 +20,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
index 231c2826..682c33e4 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/multi-imp.json
@@ -41,9 +41,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -81,9 +81,9 @@
                             "tagid": "pub1_plmt1"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "pub1"
                         }
@@ -152,9 +152,9 @@
                             "tagid": "pub2_plmt2"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "pub2"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json
index 45b35e05..642e4958 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-bid-204.json
@@ -16,9 +16,9 @@
                 }
             }
         ],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -56,9 +56,9 @@
                             "tagid": "123_456"
                         }
                     ],
-                    "site": {
-                        "domain": "prebid.org",
-                        "page": "prebid.org",
+                    "app": {
+                        "id": "app-abc",
+                        "bundle": "com.prebid",
                         "publisher": {
                             "id": "123"
                         }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json
index 7420f7e8..fccdf71c 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/no-imps.json
@@ -2,9 +2,9 @@
     "mockBidRequest": {
         "id": "test-req-id",
         "imp": [],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json
index 964dcb48..72b4fbac 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-buyeruid.json
@@ -26,9 +26,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json
index a9c3c23d..f13b70e1 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-placementId.json
@@ -25,9 +25,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json
index c50f3d36..a80a1e09 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/required-param-publisherId.json
@@ -25,9 +25,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json
index 7ff88861..f0a11905 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/server-error-500.json
@@ -14,9 +14,9 @@
                 }
             }
         }],
-        "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org"
+        "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid"
         },
         "device": {
             "ip": "152.193.6.74"
@@ -51,9 +51,9 @@
                     },
                     "tagid": "123_456"
                 }],
-                "site": {
-                    "domain": "prebid.org",
-                    "page": "prebid.org",
+                "app": {
+                    "id": "app-abc",
+                    "bundle": "com.prebid",
                     "publisher": {
                         "id": "123"
                     }
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json
new file mode 100644
index 00000000..9155352a
--- /dev/null
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/site-not-supported.json
@@ -0,0 +1,38 @@
+{
+    "mockBidRequest": {
+        "id": "test-req-id",
+        "imp": [{
+            "id": "test-imp-id",
+            "banner": {
+                "format": [{
+                    "w": 300,
+                    "h": 250
+                }],
+                "w": 300,
+                "h": 250
+            },
+            "ext": {
+                "bidder": {
+                    "publisherid": "123",
+                    "placementid": "456"
+                }
+            }
+        }],
+        "site": {
+            "domain": "prebid.org",
+            "page": "prebid.org"
+        },
+        "device": {
+            "ip": "152.193.6.74"
+        },
+        "user": {
+            "id": "db089de9-a62e-4861-a881-0ff15e052516",
+            "buyeruid": "v4_bidder_token"
+        },
+        "tmax": 500
+    },
+    "expectedMakeRequestsErrors": [{
+        "value": "Site impressions are not supported.",
+        "comparison": "literal"
+    }]
+}
\ No newline at end of file
diff --git a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json
index 34c1eccc..45c34192 100644
--- a/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json
+++ b/adapters/audienceNetwork/audienceNetworktest/supplemental/split-placementId.json
@@ -21,9 +21,9 @@
         }
       }
     ],
-    "site": {
-      "domain": "prebid.org",
-      "page": "prebid.org"
+    "app": {
+      "id": "app-abc",
+      "bundle": "com.prebid"
     },
     "device": {
       "ip": "152.193.6.74"
@@ -50,9 +50,9 @@
               "tagid": "123_456"
             }
           ],
-          "site": {
-            "domain": "prebid.org",
-            "page": "prebid.org",
+          "app": {
+            "id": "app-abc",
+            "bundle": "com.prebid",
             "publisher": {
                 "id": "123"
             }
diff --git a/adapters/audienceNetwork/facebook_test.go b/adapters/audienceNetwork/facebook_test.go
index 7f567d61..912f1222 100644
--- a/adapters/audienceNetwork/facebook_test.go
+++ b/adapters/audienceNetwork/facebook_test.go
@@ -61,23 +61,6 @@ func TestMakeTimeoutNoticeApp(t *testing.T) {
 	assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.")
 }
 
-func TestMakeTimeoutNoticeSite(t *testing.T) {
-	req := adapters.RequestData{
-		Body: []byte(`{"id":"1234","imp":[{"id":"1234"}],"site":{"publisher":{"id":"5678"}}}`),
-	}
-	fba := NewFacebookBidder("test-platform-id", "test-app-secret")
-
-	tb, ok := fba.(adapters.TimeoutBidder)
-	if !ok {
-		t.Error("Facebook adapter is not a TimeoutAdapter")
-	}
-
-	toReq, err := tb.MakeTimeoutNotification(&req)
-	assert.Nil(t, err, "Facebook MakeTimeoutNotification() return an error %v", err)
-	expectedUri := "https://www.facebook.com/audiencenetwork/nurl/?partner=test-platform-id&app=5678&auction=1234&ortb_loss_code=2"
-	assert.Equal(t, expectedUri, toReq.Uri, "Facebook timeout notification not returning the expected URI.")
-}
-
 func TestMakeTimeoutNoticeBadRequest(t *testing.T) {
 	req := adapters.RequestData{
 		Body: []byte(`{"imp":[{{"id":"1234"}}`),
diff --git a/config/config_test.go b/config/config_test.go
index 3456694d..4774d9d6 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -33,6 +33,7 @@ func TestDefaults(t *testing.T) {
 	cmpBools(t, "account_required", cfg.AccountRequired, false)
 	cmpInts(t, "metrics.influxdb.collection_rate_seconds", cfg.Metrics.Influxdb.MetricSendInterval, 20)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, false)
+	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "")
 }
 
@@ -89,6 +90,7 @@ metrics:
     metric_send_interval: 30
   disabled_metrics:
     account_adapter_details: true
+    adapter_connections_metrics: true
 datacache:
   type: postgres
   filename: /usr/db/db.db
@@ -294,6 +296,7 @@ func TestFullConfig(t *testing.T) {
 	cmpStrings(t, "adapters.rhythmone.usersync_url", cfg.Adapters[string(openrtb_ext.BidderRhythmone)].UserSyncURL, "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir=http%3A%2F%2Fprebid-server.prebid.org%2F%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BRX_UUID%5D")
 	cmpBools(t, "account_required", cfg.AccountRequired, true)
 	cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true)
+	cmpBools(t, "adapter_connections_metrics", cfg.Metrics.Disabled.AdapterConnectionMetrics, true)
 	cmpStrings(t, "certificates_file", cfg.PemCertsFile, "/etc/ssl/cert.pem")
 	cmpStrings(t, "request_validation.ipv4_private_networks", cfg.RequestValidation.IPv4PrivateNetworks[0], "1.1.1.0/24")
 	cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[0], "1111::/16")
diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go
index 731fd55e..692d3fb0 100644
--- a/endpoints/openrtb2/amp_auction_test.go
+++ b/endpoints/openrtb2/amp_auction_test.go
@@ -755,8 +755,9 @@ func TestQueryParamOverrides(t *testing.T) {
 	curl := "http://example.com"
 	slot := "1234"
 	timeout := int64(500)
+	account := "12345"
 
-	request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s&debug=1&curl=%s&slot=%s&timeout=%d", requestID, curl, slot, timeout), nil)
+	request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?tag_id=%s&debug=1&curl=%s&slot=%s&timeout=%d&account=%s", requestID, curl, slot, timeout, account), nil)
 	recorder := httptest.NewRecorder()
 	endpoint(recorder, request, nil)
 
@@ -784,6 +785,10 @@ func TestQueryParamOverrides(t *testing.T) {
 	if resolvedRequest.Site == nil || resolvedRequest.Site.Page != curl {
 		t.Errorf("Expected Site.Page to equal curl (%s), got: %s", curl, resolvedRequest.Site.Page)
 	}
+
+	if resolvedRequest.Site == nil || resolvedRequest.Site.Publisher == nil || resolvedRequest.Site.Publisher.ID != account {
+		t.Errorf("Expected Site.Publisher.ID to equal (%s), got: %s", account, resolvedRequest.Site.Publisher.ID)
+	}
 }
 
 func TestOverrideDimensions(t *testing.T) {
@@ -876,6 +881,7 @@ type formatOverrideSpec struct {
 	overrideWidth  uint64
 	overrideHeight uint64
 	multisize      string
+	account        string
 	expect         []openrtb.Format
 }
 
@@ -897,7 +903,7 @@ func (s formatOverrideSpec) execute(t *testing.T) {
 		openrtb_ext.BidderMap,
 	)
 
-	url := fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&debug=1&w=%d&h=%d&ow=%d&oh=%d&ms=%s", s.width, s.height, s.overrideWidth, s.overrideHeight, s.multisize)
+	url := fmt.Sprintf("/openrtb2/auction/amp?tag_id=1&debug=1&w=%d&h=%d&ow=%d&oh=%d&ms=%s&account=%s", s.width, s.height, s.overrideWidth, s.overrideHeight, s.multisize, s.account)
 	request := httptest.NewRequest("GET", url, nil)
 	recorder := httptest.NewRecorder()
 	endpoint(recorder, request, nil)
diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go
index d4fc0cf7..1a27b72a 100644
--- a/exchange/bidder_test.go
+++ b/exchange/bidder_test.go
@@ -6,8 +6,11 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
+	"net/http/httptrace"
+	"strings"
 	"testing"
 	"time"
 
@@ -17,8 +20,11 @@ import (
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/currencies"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/pbsmetrics"
+	metricsConf "github.com/prebid/prebid-server/pbsmetrics/config"
 	metricsConfig "github.com/prebid/prebid-server/pbsmetrics/config"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 
 	nativeRequests "github.com/mxmCherry/openrtb/native/request"
 	nativeResponse "github.com/mxmCherry/openrtb/native/response"
@@ -68,7 +74,7 @@ func TestSingleBidder(t *testing.T) {
 		},
 		bidResponse: mockBidderResponse,
 	}
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 	currencyConverter := currencies.NewRateConverterDefault()
 	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
 
@@ -156,7 +162,7 @@ func TestMultiBidder(t *testing.T) {
 			}},
 		bidResponse: mockBidderResponse,
 	}
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 	currencyConverter := currencies.NewRateConverterDefault()
 	seatBid, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
 
@@ -194,8 +200,10 @@ func TestBidderTimeout(t *testing.T) {
 	defer server.Close()
 
 	bidder := &bidderAdapter{
-		Bidder: &mixedMultiBidder{},
-		Client: server.Client(),
+		Bidder:     &mixedMultiBidder{},
+		BidderName: openrtb_ext.BidderAppnexus,
+		Client:     server.Client(),
+		me:         &metricsConf.DummyMetricsEngine{},
 	}
 
 	callInfo := bidder.doRequest(ctx, &adapters.RequestData{
@@ -235,8 +243,10 @@ func TestConnectionClose(t *testing.T) {
 	server = httptest.NewServer(handler)
 
 	bidder := &bidderAdapter{
-		Bidder: &mixedMultiBidder{},
-		Client: server.Client(),
+		Bidder:     &mixedMultiBidder{},
+		Client:     server.Client(),
+		BidderName: openrtb_ext.BidderAppnexus,
+		me:         &metricsConf.DummyMetricsEngine{},
 	}
 
 	callInfo := bidder.doRequest(context.Background(), &adapters.RequestData{
@@ -514,7 +524,7 @@ func TestMultiCurrencies(t *testing.T) {
 		)
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 		currencyConverter := currencies.NewRateConverter(
 			&http.Client{},
 			mockedHTTPServer.URL,
@@ -663,7 +673,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
 		}
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 		currencyConverter := currencies.NewRateConverterDefault()
 		seatBid, errs := bidder.requestBid(
 			context.Background(),
@@ -829,7 +839,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
 		}
 
 		// Execute:
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 		currencyConverter := currencies.NewRateConverter(
 			&http.Client{},
 			mockedHTTPServer.URL,
@@ -945,7 +955,7 @@ func TestServerCallDebugging(t *testing.T) {
 			Headers: http.Header{},
 		},
 	}
-	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 	currencyConverter := currencies.NewRateConverterDefault()
 
 	bids, _ := bidder.requestBid(
@@ -1057,7 +1067,7 @@ func TestMobileNativeTypes(t *testing.T) {
 			},
 			bidResponse: tc.mockBidderResponse,
 		}
-		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+		bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 		currencyConverter := currencies.NewRateConverterDefault()
 
 		seatBids, _ := bidder.requestBid(
@@ -1078,7 +1088,7 @@ func TestMobileNativeTypes(t *testing.T) {
 }
 
 func TestErrorReporting(t *testing.T) {
-	bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+	bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 	currencyConverter := currencies.NewRateConverterDefault()
 	bids, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{})
 	if bids != nil {
@@ -1233,6 +1243,82 @@ func TestSetAssetTypes(t *testing.T) {
 	}
 }
 
+func TestCallRecordAdapterConnections(t *testing.T) {
+	// Setup mock server
+	respStatus := 200
+	respBody := "{\"bid\":false}"
+	server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody))
+	defer server.Close()
+
+	// declare requestBid parameters
+	bidAdjustment := 2.0
+
+	bidderImpl := &goodSingleBidder{
+		httpRequest: &adapters.RequestData{
+			Method:  "POST",
+			Uri:     server.URL,
+			Body:    []byte("{\"key\":\"val\"}"),
+			Headers: http.Header{},
+		},
+		bidResponse: &adapters.BidderResponse{},
+	}
+
+	// setup a mock metrics engine and its expectation
+	metrics := &pbsmetrics.MetricsEngineMock{}
+	expectedAdapterName := openrtb_ext.BidderAppnexus
+	compareConnWaitTime := func(dur time.Duration) bool { return dur.Nanoseconds() > 0 }
+
+	metrics.On("RecordAdapterConnections", expectedAdapterName, false, mock.MatchedBy(compareConnWaitTime)).Once()
+
+	// Run requestBid using an http.Client with a mock handler
+	bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus)
+	_, errs := bidder.requestBid(context.Background(), &openrtb.BidRequest{}, "test", bidAdjustment, currencies.NewRateConverterDefault().Rates(), &adapters.ExtraRequestInfo{})
+
+	// Assert no errors
+	assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs)
+
+	// Assert RecordAdapterConnections() was called with the parameters we expected
+	metrics.AssertExpectations(t)
+}
+
+type DNSDoneTripper struct{}
+
+func (DNSDoneTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+	//Access the httptrace.ClientTrace
+	trace := httptrace.ContextClientTrace(req.Context())
+
+	//Force DNSDone call defined in exchange/bidder.go
+	trace.DNSDone(httptrace.DNSDoneInfo{})
+
+	resp := &http.Response{
+		StatusCode: 200,
+		Header:     map[string][]string{"Location": {"http://www.example.com/"}},
+		Body:       ioutil.NopCloser(strings.NewReader("postBody")),
+	}
+	return resp, nil
+}
+
+func TestCallRecordRecordDNSTime(t *testing.T) {
+	// setup a mock metrics engine and its expectation
+	metricsMock := &pbsmetrics.MetricsEngineMock{}
+	metricsMock.Mock.On("RecordDNSTime", mock.Anything).Return()
+
+	// Instantiate the bidder that will send the request. We'll make sure to use an
+	// http.Client that runs our mock RoundTripper so DNSDone(httptrace.DNSDoneInfo{})
+	// gets called
+	bidder := &bidderAdapter{
+		Bidder: &mixedMultiBidder{},
+		Client: &http.Client{Transport: DNSDoneTripper{}},
+		me:     metricsMock,
+	}
+
+	// Run test
+	bidder.doRequest(context.Background(), &adapters.RequestData{Method: "POST", Uri: "http://www.example.com/"})
+
+	// Tried one or another, none seem to work without panicking
+	metricsMock.AssertExpectations(t)
+}
+
 func TestTimeoutNotificationOff(t *testing.T) {
 	respBody := "{\"bid\":false}"
 	respStatus := 200
@@ -1248,10 +1334,10 @@ func TestTimeoutNotificationOff(t *testing.T) {
 		},
 	}
 	bidder := &bidderAdapter{
-		Bidder:      bidderImpl,
-		Client:      server.Client(),
-		DebugConfig: config.Debug{},
-		me:          &metricsConfig.DummyMetricsEngine{},
+		Bidder: bidderImpl,
+		Client: server.Client(),
+		config: bidderAdapterConfig{Debug: config.Debug{}},
+		me:     &metricsConf.DummyMetricsEngine{},
 	}
 	if tb, ok := bidder.Bidder.(adapters.TimeoutBidder); !ok {
 		t.Error("Failed to cast bidder to a TimeoutBidder")
@@ -1284,13 +1370,15 @@ func TestTimeoutNotificationOn(t *testing.T) {
 	bidderAdapter := &bidderAdapter{
 		Bidder: bidderWrappedWithInfo,
 		Client: server.Client(),
-		DebugConfig: config.Debug{
-			TimeoutNotification: config.TimeoutNotification{
-				Log:          true,
-				SamplingRate: 1.0,
+		config: bidderAdapterConfig{
+			Debug: config.Debug{
+				TimeoutNotification: config.TimeoutNotification{
+					Log:          true,
+					SamplingRate: 1.0,
+				},
 			},
 		},
-		me: &metricsConfig.DummyMetricsEngine{},
+		me: &metricsConf.DummyMetricsEngine{},
 	}
 
 	// Unwrap To Mimic exchange.go Casting Code
diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go
index 72de1d42..16955e97 100644
--- a/exchange/targeting_test.go
+++ b/exchange/targeting_test.go
@@ -134,7 +134,7 @@ func buildAdapterMap(bids map[openrtb_ext.BidderName][]*openrtb.Bid, mockServerU
 		adapterMap[bidder] = adaptBidder(&mockTargetingBidder{
 			mockServerURL: mockServerURL,
 			bids:          bids,
-		}, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{})
+		}, client, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus)
 	}
 	return adapterMap
 }
diff --git a/pbsmetrics/go_metrics_test.go b/pbsmetrics/go_metrics_test.go
index 2faa0849..f6769916 100644
--- a/pbsmetrics/go_metrics_test.go
+++ b/pbsmetrics/go_metrics_test.go
@@ -2,6 +2,7 @@ package pbsmetrics
 
 import (
 	"testing"
+	"time"
 
 	"github.com/prebid/prebid-server/config"
 	"github.com/prebid/prebid-server/openrtb_ext"
@@ -115,6 +116,10 @@ func ensureContainsAdapterMetrics(t *testing.T, registry metrics.Registry, name
 	ensureContains(t, registry, name+".request_time", adapterMetrics.RequestTimer)
 	ensureContains(t, registry, name+".prices", adapterMetrics.PriceHistogram)
 	ensureContainsBidTypeMetrics(t, registry, name, adapterMetrics.MarkupMetrics)
+
+	ensureContains(t, registry, name+".connections_created", adapterMetrics.ConnCreated)
+	ensureContains(t, registry, name+".connections_reused", adapterMetrics.ConnReused)
+	ensureContains(t, registry, name+".connection_wait_time", adapterMetrics.ConnWaitTime)
 }
 
 func TestRecordBidTypeDisabledConfig(t *testing.T) {
@@ -179,6 +184,140 @@ func TestRecordBidTypeDisabledConfig(t *testing.T) {
 	}
 }
 
+func TestRecordDNSTime(t *testing.T) {
+	testCases := []struct {
+		description         string
+		inDnsLookupDuration time.Duration
+		outExpDuration      time.Duration
+	}{
+		{
+			description:         "Five second DNS lookup time",
+			inDnsLookupDuration: time.Second * 5,
+			outExpDuration:      time.Second * 5,
+		},
+		{
+			description:         "Zero DNS lookup time",
+			inDnsLookupDuration: time.Duration(0),
+			outExpDuration:      time.Duration(0),
+		},
+	}
+	for _, test := range testCases {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true})
+
+		m.RecordDNSTime(test.inDnsLookupDuration)
+
+		assert.Equal(t, test.outExpDuration.Nanoseconds(), m.DNSLookupTimer.Sum(), test.description)
+	}
+}
+
+func TestRecordAdapterConnections(t *testing.T) {
+	var fakeBidder openrtb_ext.BidderName = "fooAdvertising"
+
+	type testIn struct {
+		adapterName         openrtb_ext.BidderName
+		connWasReused       bool
+		connWait            time.Duration
+		connMetricsDisabled bool
+	}
+
+	type testOut struct {
+		expectedConnReusedCount  int64
+		expectedConnCreatedCount int64
+		expectedConnWaitTime     time.Duration
+	}
+
+	testCases := []struct {
+		description string
+		in          testIn
+		out         testOut
+	}{
+		{
+			description: "Successful, new connection created, has connection wait",
+			in: testIn{
+				adapterName:         openrtb_ext.BidderAppnexus,
+				connWasReused:       false,
+				connWait:            time.Second * 5,
+				connMetricsDisabled: false,
+			},
+			out: testOut{
+				expectedConnReusedCount:  0,
+				expectedConnCreatedCount: 1,
+				expectedConnWaitTime:     time.Second * 5,
+			},
+		},
+		{
+			description: "Successful, new connection created, has connection wait",
+			in: testIn{
+				adapterName:         openrtb_ext.BidderAppnexus,
+				connWasReused:       false,
+				connWait:            time.Second * 4,
+				connMetricsDisabled: false,
+			},
+			out: testOut{
+				expectedConnCreatedCount: 1,
+				expectedConnWaitTime:     time.Second * 4,
+			},
+		},
+		{
+			description: "Successful, was reused, no connection wait",
+			in: testIn{
+				adapterName:         openrtb_ext.BidderAppnexus,
+				connWasReused:       true,
+				connMetricsDisabled: false,
+			},
+			out: testOut{
+				expectedConnReusedCount: 1,
+				expectedConnWaitTime:    0,
+			},
+		},
+		{
+			description: "Successful, was reused, has connection wait",
+			in: testIn{
+				adapterName:         openrtb_ext.BidderAppnexus,
+				connWasReused:       true,
+				connWait:            time.Second * 5,
+				connMetricsDisabled: false,
+			},
+			out: testOut{
+				expectedConnReusedCount: 1,
+				expectedConnWaitTime:    time.Second * 5,
+			},
+		},
+		{
+			description: "Fake bidder, nothing gets updated",
+			in: testIn{
+				adapterName:         fakeBidder,
+				connWasReused:       false,
+				connWait:            0,
+				connMetricsDisabled: false,
+			},
+			out: testOut{},
+		},
+		{
+			description: "Adapter connection metrics are disabled, nothing gets updated",
+			in: testIn{
+				adapterName:         openrtb_ext.BidderAppnexus,
+				connWasReused:       false,
+				connWait:            time.Second * 5,
+				connMetricsDisabled: true,
+			},
+			out: testOut{},
+		},
+	}
+
+	for i, test := range testCases {
+		registry := metrics.NewRegistry()
+		m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AdapterConnectionMetrics: test.in.connMetricsDisabled})
+
+		m.RecordAdapterConnections(test.in.adapterName, test.in.connWasReused, test.in.connWait)
+
+		assert.Equal(t, test.out.expectedConnReusedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnReused.Count(), "Test [%d] incorrect number of reused connections to adapter", i)
+		assert.Equal(t, test.out.expectedConnCreatedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnCreated.Count(), "Test [%d] incorrect number of new connections to adapter created", i)
+		assert.Equal(t, test.out.expectedConnWaitTime.Nanoseconds(), m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnWaitTime.Sum(), "Test [%d] incorrect wait time in connection to adapter", i)
+	}
+}
+
 func TestNewMetricsWithDisabledConfig(t *testing.T) {
 	registry := metrics.NewRegistry()
 	m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true})
diff --git a/pbsmetrics/prometheus/prometheus_test.go b/pbsmetrics/prometheus/prometheus_test.go
index b722ab28..b6153b16 100644
--- a/pbsmetrics/prometheus/prometheus_test.go
+++ b/pbsmetrics/prometheus/prometheus_test.go
@@ -1,6 +1,7 @@
 package prometheusmetrics
 
 import (
+	"fmt"
 	"testing"
 	"time"
 
@@ -17,7 +18,7 @@ func createMetricsForTesting() *Metrics {
 		Port:      8080,
 		Namespace: "prebid",
 		Subsystem: "server",
-	})
+	}, config.DisabledMetrics{})
 }
 
 func TestMetricCountGatekeeping(t *testing.T) {
@@ -61,7 +62,7 @@ func TestMetricCountGatekeeping(t *testing.T) {
 	// Verify Per-Adapter Cardinality
 	// - This assertion provides a warning for newly added adapter metrics. Threre are 40+ adapters which makes the
 	//   cost of new per-adapter metrics rather expensive. Thought should be given when adding new per-adapter metrics.
-	assert.True(t, perAdapterCardinalityCount <= 22, "Per-Adapter Cardinality")
+	assert.True(t, perAdapterCardinalityCount <= 27, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount)
 }
 
 func TestConnectionMetrics(t *testing.T) {
@@ -944,6 +945,175 @@ func TestTimeoutNotifications(t *testing.T) {
 
 }
 
+func TestRecordDNSTime(t *testing.T) {
+	type testIn struct {
+		dnsLookupDuration time.Duration
+	}
+	type testOut struct {
+		expDuration float64
+		expCount    uint64
+	}
+	testCases := []struct {
+		description string
+		in          testIn
+		out         testOut
+	}{
+		{
+			description: "Five second DNS lookup time",
+			in: testIn{
+				dnsLookupDuration: time.Second * 5,
+			},
+			out: testOut{
+				expDuration: 5,
+				expCount:    1,
+			},
+		},
+		{
+			description: "Zero DNS lookup time",
+			in:          testIn{},
+			out: testOut{
+				expDuration: 0,
+				expCount:    1,
+			},
+		},
+	}
+	for i, test := range testCases {
+		pm := createMetricsForTesting()
+		pm.RecordDNSTime(test.in.dnsLookupDuration)
+
+		m := dto.Metric{}
+		pm.dnsLookupTimer.Write(&m)
+		histogram := *m.GetHistogram()
+
+		assert.Equal(t, test.out.expCount, histogram.GetSampleCount(), "[%d] Incorrect number of histogram entries. Desc: %s\n", i, test.description)
+		assert.Equal(t, test.out.expDuration, histogram.GetSampleSum(), "[%d] Incorrect number of histogram cumulative values. Desc: %s\n", i, test.description)
+	}
+}
+
+func TestRecordAdapterConnections(t *testing.T) {
+
+	type testIn struct {
+		adapterName   openrtb_ext.BidderName
+		connWasReused bool
+		connWait      time.Duration
+	}
+
+	type testOut struct {
+		expectedConnReusedCount  int64
+		expectedConnCreatedCount int64
+		expectedConnWaitCount    uint64
+		expectedConnWaitTime     float64
+	}
+
+	testCases := []struct {
+		description string
+		in          testIn
+		out         testOut
+	}{
+		{
+			description: "[1] Successful, new connection created, was idle, has connection wait",
+			in: testIn{
+				adapterName:   openrtb_ext.BidderAppnexus,
+				connWasReused: false,
+				connWait:      time.Second * 5,
+			},
+			out: testOut{
+				expectedConnReusedCount:  0,
+				expectedConnCreatedCount: 1,
+				expectedConnWaitCount:    1,
+				expectedConnWaitTime:     5,
+			},
+		},
+		{
+			description: "[2] Successful, new connection created, not idle, has connection wait",
+			in: testIn{
+				adapterName:   openrtb_ext.BidderAppnexus,
+				connWasReused: false,
+				connWait:      time.Second * 4,
+			},
+			out: testOut{
+				expectedConnReusedCount:  0,
+				expectedConnCreatedCount: 1,
+				expectedConnWaitCount:    1,
+				expectedConnWaitTime:     4,
+			},
+		},
+		{
+			description: "[3] Successful, was reused, was idle, no connection wait",
+			in: testIn{
+				adapterName:   openrtb_ext.BidderAppnexus,
+				connWasReused: true,
+			},
+			out: testOut{
+				expectedConnReusedCount:  1,
+				expectedConnCreatedCount: 0,
+				expectedConnWaitCount:    1,
+				expectedConnWaitTime:     0,
+			},
+		},
+		{
+			description: "[4] Successful, was reused, not idle, has connection wait",
+			in: testIn{
+				adapterName:   openrtb_ext.BidderAppnexus,
+				connWasReused: true,
+				connWait:      time.Second * 5,
+			},
+			out: testOut{
+				expectedConnReusedCount:  1,
+				expectedConnCreatedCount: 0,
+				expectedConnWaitCount:    1,
+				expectedConnWaitTime:     5,
+			},
+		},
+	}
+
+	for i, test := range testCases {
+		m := createMetricsForTesting()
+		assertDesciptions := []string{
+			fmt.Sprintf("[%d] Metric: adapterReusedConnections; Desc: %s", i+1, test.description),
+			fmt.Sprintf("[%d] Metric: adapterCreatedConnections; Desc: %s", i+1, test.description),
+			fmt.Sprintf("[%d] Metric: adapterWaitConnectionCount; Desc: %s", i+1, test.description),
+			fmt.Sprintf("[%d] Metric: adapterWaitConnectionTime; Desc: %s", i+1, test.description),
+		}
+
+		m.RecordAdapterConnections(test.in.adapterName, test.in.connWasReused, test.in.connWait)
+
+		// Assert number of reused connections
+		assertCounterVecValue(t,
+			assertDesciptions[0],
+			"adapter_connection_reused",
+			m.adapterReusedConnections,
+			float64(test.out.expectedConnReusedCount),
+			prometheus.Labels{adapterLabel: string(test.in.adapterName)})
+
+		// Assert number of new created connections
+		assertCounterVecValue(t,
+			assertDesciptions[1],
+			"adapter_connection_created",
+			m.adapterCreatedConnections,
+			float64(test.out.expectedConnCreatedCount),
+			prometheus.Labels{adapterLabel: string(test.in.adapterName)})
+
+		// Assert connection wait time
+		histogram := getHistogramFromHistogramVec(m.adapterConnectionWaitTime, adapterLabel, string(test.in.adapterName))
+		assert.Equal(t, test.out.expectedConnWaitCount, histogram.GetSampleCount(), assertDesciptions[2])
+		assert.Equal(t, test.out.expectedConnWaitTime, histogram.GetSampleSum(), assertDesciptions[3])
+	}
+}
+
+func TestDisableAdapterConnections(t *testing.T) {
+	prometheusMetrics := NewMetrics(config.PrometheusMetrics{
+		Port:      8080,
+		Namespace: "prebid",
+		Subsystem: "server",
+	}, config.DisabledMetrics{AdapterConnectionMetrics: true})
+
+	// Assert counter vector was not initialized
+	assert.Nil(t, prometheusMetrics.adapterReusedConnections, "Counter Vector adapterReusedConnections should be nil")
+	assert.Nil(t, prometheusMetrics.adapterCreatedConnections, "Counter Vector adapterCreatedConnections should be nil")
+	assert.Nil(t, prometheusMetrics.adapterConnectionWaitTime, "Counter Vector adapterConnectionWaitTime should be nil")
+}
+
 func TestRecordRequestPrivacy(t *testing.T) {
 	m := createMetricsForTesting()