Skip to content

Commit

Permalink
Implement analytics by code metrics (#1281)
Browse files Browse the repository at this point in the history
  • Loading branch information
SerhiiNahornyi authored May 24, 2021
1 parent e8e7bad commit 2147e59
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.prebid.server.analytics;

import io.vertx.core.Future;

/**
* Type of component that does transactional logging.
*/
Expand All @@ -11,7 +13,7 @@ public interface AnalyticsReporter {
* <p>
* Implementation note: this method is executed on Vert.x event loop thread so it must never use blocking API.
*/
<T> void processEvent(T event);
<T> Future<Void> processEvent(T event);

/**
* Method for defining analytics reporter ID for TCF checks.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Site;
import io.netty.channel.ConnectTimeoutException;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.apache.commons.collections4.CollectionUtils;
import org.prebid.server.analytics.model.AmpEvent;
import org.prebid.server.analytics.model.AuctionEvent;
import org.prebid.server.analytics.model.CookieSyncEvent;
import org.prebid.server.analytics.model.NotificationEvent;
import org.prebid.server.analytics.model.SetuidEvent;
import org.prebid.server.analytics.model.VideoEvent;
import org.prebid.server.auction.PrivacyEnforcementService;
import org.prebid.server.auction.model.AuctionContext;
import org.prebid.server.log.ConditionalLogger;
import org.prebid.server.metric.MetricName;
import org.prebid.server.metric.Metrics;
import org.prebid.server.privacy.gdpr.model.PrivacyEnforcementAction;
import org.prebid.server.privacy.gdpr.model.TcfContext;
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
Expand All @@ -25,6 +34,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

/**
Expand All @@ -39,24 +49,27 @@ public class AnalyticsReporterDelegator {
private final List<AnalyticsReporter> delegates;
private final Vertx vertx;
private final PrivacyEnforcementService privacyEnforcementService;
private final Metrics metrics;

private final Set<Integer> reporterVendorIds;
private final Set<String> reporterNames;

public AnalyticsReporterDelegator(List<AnalyticsReporter> delegates,
Vertx vertx,
PrivacyEnforcementService privacyEnforcementService) {
PrivacyEnforcementService privacyEnforcementService,
Metrics metrics) {
this.delegates = Objects.requireNonNull(delegates);
this.vertx = Objects.requireNonNull(vertx);
this.privacyEnforcementService = Objects.requireNonNull(privacyEnforcementService);
this.metrics = Objects.requireNonNull(metrics);

reporterVendorIds = delegates.stream().map(AnalyticsReporter::vendorId).collect(Collectors.toSet());
reporterNames = delegates.stream().map(AnalyticsReporter::name).collect(Collectors.toSet());
}

public <T> void processEvent(T event) {
for (AnalyticsReporter analyticsReporter : delegates) {
vertx.runOnContext(ignored -> analyticsReporter.processEvent(event));
vertx.runOnContext(ignored -> processEventByReporter(analyticsReporter, event));
}
}

Expand All @@ -80,7 +93,7 @@ private <T> void delegateEvent(T event,
final PrivacyEnforcementAction reporterPrivacyAction = privacyEnforcementActionMap
.getOrDefault(reporterVendorId, PrivacyEnforcementAction.restrictAll());
if (!reporterPrivacyAction.isBlockAnalyticsReport()) {
vertx.runOnContext(ignored -> analyticsReporter.processEvent(updatedEvent));
vertx.runOnContext(ignored -> processEventByReporter(analyticsReporter, updatedEvent));
}
}
} else {
Expand Down Expand Up @@ -168,4 +181,47 @@ private static ObjectNode prepareAnalytics(ObjectNode analytics, String adapterN

return !analyticsNodeCopy.isEmpty() ? analyticsNodeCopy : null;
}

private <T> void processEventByReporter(AnalyticsReporter analyticsReporter, T event) {
final String reporterName = analyticsReporter.name();
analyticsReporter.processEvent(event)
.map(ignored -> processSuccess(event, reporterName))
.otherwise(exception -> processFail(exception, event, reporterName));
}

private <T> Future<Void> processSuccess(T event, String reporterName) {
updateMetricsByEventType(event, reporterName, MetricName.ok);
return Future.succeededFuture();
}

private <T> Future<Void> processFail(Throwable exception, T event, String reporterName) {
final MetricName failedResult;
if (exception instanceof TimeoutException || exception instanceof ConnectTimeoutException) {
failedResult = MetricName.timeout;
} else {
failedResult = MetricName.err;
}
updateMetricsByEventType(event, reporterName, failedResult);
return Future.failedFuture(exception);
}

private <T> void updateMetricsByEventType(T event, String analyticsCode, MetricName result) {
final MetricName eventType;
if (event instanceof AuctionEvent) {
eventType = MetricName.event_auction;
} else if (event instanceof AmpEvent) {
eventType = MetricName.event_amp;
} else if (event instanceof VideoEvent) {
eventType = MetricName.event_video;
} else if (event instanceof SetuidEvent) {
eventType = MetricName.event_setuid;
} else if (event instanceof CookieSyncEvent) {
eventType = MetricName.event_cookie_sync;
} else if (event instanceof NotificationEvent) {
eventType = MetricName.event_notification;
} else {
eventType = MetricName.event_unknown;
}
metrics.updateAnalyticEventMetric(analyticsCode, eventType, result);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.analytics;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import io.vertx.core.Future;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -28,7 +29,7 @@ public LogAnalyticsReporter(JacksonMapper mapper) {
}

@Override
public <T> void processEvent(T event) {
public <T> Future<Void> processEvent(T event) {
final LogEvent<?> logEvent;

if (event instanceof AuctionEvent) {
Expand All @@ -49,6 +50,8 @@ public <T> void processEvent(T event) {
}

logger.debug(mapper.encode(logEvent));

return Future.succeededFuture();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.prebid.server.metric;

import com.codahale.metrics.MetricRegistry;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

/**
* AnalyticsReporter metrics support.
*/
class AnalyticsReporterMetrics extends UpdatableMetrics {

private final Function<MetricName, EventTypeMetrics> eventTypeMetricsCreator;
private final Map<MetricName, EventTypeMetrics> eventTypeMetrics;

AnalyticsReporterMetrics(MetricRegistry metricRegistry, CounterType counterType, String analyticCode) {
super(Objects.requireNonNull(metricRegistry), Objects.requireNonNull(counterType),
nameCreator(createAdapterPrefix(Objects.requireNonNull(analyticCode))));

eventTypeMetricsCreator = eventType ->
new EventTypeMetrics(metricRegistry, counterType, createAdapterPrefix(analyticCode), eventType);
eventTypeMetrics = new HashMap<>();
}

private static String createAdapterPrefix(String reporterName) {
return String.format("analytics.%s", reporterName);
}

private static Function<MetricName, String> nameCreator(String prefix) {
return metricName -> String.format("%s.%s", prefix, metricName.toString());
}

EventTypeMetrics forEventType(MetricName eventType) {
return eventTypeMetrics.computeIfAbsent(eventType, eventTypeMetricsCreator);
}
}
19 changes: 19 additions & 0 deletions src/main/java/org/prebid/server/metric/EventTypeMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.prebid.server.metric;

import com.codahale.metrics.MetricRegistry;

import java.util.function.Function;

/**
* Metrics for reporting on certain event type
*/
public class EventTypeMetrics extends UpdatableMetrics {

EventTypeMetrics(MetricRegistry metricRegistry, CounterType counterType, String prefix, MetricName eventType) {
super(metricRegistry, counterType, nameCreator(prefix, eventType));
}

private static Function<MetricName, String> nameCreator(String prefix, MetricName eventType) {
return metricName -> String.format("%s.%s.%s", prefix, eventType, metricName.toString());
}
}
8 changes: 8 additions & 0 deletions src/main/java/org/prebid/server/metric/MetricName.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public enum MetricName {
cookiesync,
setuid,

// event types
event_auction("auction"),
event_amp("amp"),
event_video("video"),
event_notification("event"),
event_cookie_sync("cookie_sync"),
event_setuid("setuid"),
event_unknown("unknown"),

// request and adapter statuses
ok,
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/prebid/server/metric/Metrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class Metrics extends UpdatableMetrics {
private final Function<MetricName, RequestStatusMetrics> requestMetricsCreator;
private final Function<String, AccountMetrics> accountMetricsCreator;
private final Function<String, AdapterTypeMetrics> adapterMetricsCreator;
private final Function<String, AnalyticsReporterMetrics> analyticMetricsCreator;
private final Function<Integer, BidderCardinalityMetrics> bidderCardinalityMetricsCreator;
private final Function<MetricName, CircuitBreakerMetrics> circuitBreakerMetricsCreator;
private final Function<MetricName, SettingsCacheMetrics> settingsCacheMetricsCreator;
Expand All @@ -37,6 +38,7 @@ public class Metrics extends UpdatableMetrics {
private final Map<MetricName, RequestStatusMetrics> requestMetrics;
private final Map<String, AccountMetrics> accountMetrics;
private final Map<String, AdapterTypeMetrics> adapterMetrics;
private final Map<String, AnalyticsReporterMetrics> analyticMetrics;
private final Map<Integer, BidderCardinalityMetrics> bidderCardinailtyMetrics;
private final UserSyncMetrics userSyncMetrics;
private final CookieSyncMetrics cookieSyncMetrics;
Expand All @@ -58,11 +60,14 @@ public Metrics(MetricRegistry metricRegistry, CounterType counterType,
adapterMetricsCreator = adapterType -> new AdapterTypeMetrics(metricRegistry, counterType, adapterType);
bidderCardinalityMetricsCreator = cardinality -> new BidderCardinalityMetrics(
metricRegistry, counterType, cardinality);
analyticMetricsCreator = analyticCode -> new AnalyticsReporterMetrics(
metricRegistry, counterType, analyticCode);
circuitBreakerMetricsCreator = type -> new CircuitBreakerMetrics(metricRegistry, counterType, type);
settingsCacheMetricsCreator = type -> new SettingsCacheMetrics(metricRegistry, counterType, type);
requestMetrics = new EnumMap<>(MetricName.class);
accountMetrics = new HashMap<>();
adapterMetrics = new HashMap<>();
analyticMetrics = new HashMap<>();
bidderCardinailtyMetrics = new HashMap<>();
userSyncMetrics = new UserSyncMetrics(metricRegistry, counterType);
cookieSyncMetrics = new CookieSyncMetrics(metricRegistry, counterType);
Expand Down Expand Up @@ -90,6 +95,10 @@ AdapterTypeMetrics forAdapter(String adapterType) {
return adapterMetrics.computeIfAbsent(adapterType, adapterMetricsCreator);
}

AnalyticsReporterMetrics forAnalyticReporter(String analyticCode) {
return analyticMetrics.computeIfAbsent(analyticCode, analyticMetricsCreator);
}

UserSyncMetrics userSync() {
return userSyncMetrics;
}
Expand Down Expand Up @@ -261,6 +270,10 @@ public void updateAdapterRequestErrorMetric(String bidder, MetricName errorMetri
forAdapter(bidder).request().incCounter(errorMetric);
}

public void updateAnalyticEventMetric(String analyticCode, MetricName eventType, MetricName result) {
forAnalyticReporter(analyticCode).forEventType(eventType).incCounter(result);
}

public void updateSizeValidationMetrics(String bidder, String accountId, MetricName type) {
forAdapter(bidder).response().validation().size().incCounter(type);
forAccount(accountId).response().validation().size().incCounter(type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.prebid.server.analytics.LogAnalyticsReporter;
import org.prebid.server.auction.PrivacyEnforcementService;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.metric.Metrics;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
Expand All @@ -21,12 +22,14 @@ public class AnalyticsConfiguration {
AnalyticsReporterDelegator analyticsReporterDelegator(
@Autowired(required = false) List<AnalyticsReporter> delegates,
Vertx vertx,
PrivacyEnforcementService privacyEnforcementService) {
PrivacyEnforcementService privacyEnforcementService,
Metrics metrics) {

return new AnalyticsReporterDelegator(
delegates != null ? delegates : Collections.emptyList(),
vertx,
privacyEnforcementService);
privacyEnforcementService,
metrics);
}

@Bean
Expand Down
Loading

0 comments on commit 2147e59

Please sign in to comment.