Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement analytics by code metrics #1281

Merged
merged 4 commits into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
SerhiiNahornyi marked this conversation as resolved.
Show resolved Hide resolved
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