Add card dealt metrics.

This commit is contained in:
Andy Janata 2018-02-25 22:01:21 -08:00
parent 89a4ceeeef
commit adab3b7001
4 changed files with 64 additions and 25 deletions

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2012-2017, Andy Janata * Copyright (c) 2012-2018, Andy Janata
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted * Redistribution and use in source and binary forms, with or without modification, are permitted
@ -41,6 +41,13 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import com.google.inject.Inject;
import com.google.inject.Provider;
import net.socialgamer.cah.CahModule.UniqueId; import net.socialgamer.cah.CahModule.UniqueId;
import net.socialgamer.cah.Constants.BlackCardData; import net.socialgamer.cah.Constants.BlackCardData;
import net.socialgamer.cah.Constants.ErrorCode; import net.socialgamer.cah.Constants.ErrorCode;
@ -59,13 +66,6 @@ import net.socialgamer.cah.data.QueuedMessage.MessageType;
import net.socialgamer.cah.metrics.Metrics; import net.socialgamer.cah.metrics.Metrics;
import net.socialgamer.cah.task.SafeTimerTask; import net.socialgamer.cah.task.SafeTimerTask;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import com.google.inject.Inject;
import com.google.inject.Provider;
/** /**
* Game data and logic class. Games are simple finite state machines, with 3 states that wait for * Game data and logic class. Games are simple finite state machines, with 3 states that wait for
@ -178,6 +178,11 @@ public class Game {
private final Provider<CardcastService> cardcastServiceProvider; private final Provider<CardcastService> cardcastServiceProvider;
private final Provider<String> uniqueIdProvider; private final Provider<String> uniqueIdProvider;
private String currentUniqueId; private String currentUniqueId;
/**
* Sequence number of cards dealt. This allows re-shuffles and re-deals to still be tracked as
* unique card deals.
*/
private long dealSeq = 0;
/** /**
* Create a new game. * Create a new game.
@ -799,6 +804,7 @@ public class Game {
final WhiteCard card = getNextWhiteCard(); final WhiteCard card = getNextWhiteCard();
hand.add(card); hand.add(card);
newCards.add(card); newCards.add(card);
metrics.cardDealt(currentUniqueId, player.getUser().getSessionId(), card, dealSeq++);
} }
sendCardsToPlayer(player, newCards); sendCardsToPlayer(player, newCards);
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2017, Andy Janata * Copyright (c) 2017-2018, Andy Janata
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted * Redistribution and use in source and binary forms, with or without modification, are permitted
@ -36,13 +36,6 @@ import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.socialgamer.cah.data.BlackCard;
import net.socialgamer.cah.data.CardSet;
import net.socialgamer.cah.data.WhiteCard;
import net.socialgamer.cah.db.PyxBlackCard;
import net.socialgamer.cah.db.PyxCardSet;
import net.socialgamer.cah.db.PyxWhiteCard;
import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.KafkaProducer;
@ -61,6 +54,13 @@ import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.model.CityResponse;
import net.socialgamer.cah.data.BlackCard;
import net.socialgamer.cah.data.CardSet;
import net.socialgamer.cah.data.WhiteCard;
import net.socialgamer.cah.db.PyxBlackCard;
import net.socialgamer.cah.db.PyxCardSet;
import net.socialgamer.cah.db.PyxWhiteCard;
/** /**
* Metrics implementation that sends all data to an Apache Kafka topic. * Metrics implementation that sends all data to an Apache Kafka topic.
@ -70,7 +70,9 @@ import com.maxmind.geoip2.model.CityResponse;
@Singleton @Singleton
public class KafkaMetrics implements Metrics { public class KafkaMetrics implements Metrics {
private static final String metricsVersion = "0.1"; // 0.1: initial version
// 0.2: added cardDealt
private static final String metricsVersion = "0.2";
private static final Logger LOG = Logger.getLogger(KafkaMetrics.class); private static final Logger LOG = Logger.getLogger(KafkaMetrics.class);
private final ProducerCallback callback = new ProducerCallback(); private final ProducerCallback callback = new ProducerCallback();
@ -367,4 +369,27 @@ public class KafkaMetrics implements Metrics {
send(getEventMap("roundComplete", data)); send(getEventMap("roundComplete", data));
} }
@Override
public void cardDealt(final String gameId, final String sessionId, final WhiteCard card,
final long dealSeq) {
trace("%s, %s, %s, %d", gameId, sessionId, card, dealSeq);
final Map<String, Object> data = new HashMap<>();
data.put("gameId", gameId);
data.put("sessionId", sessionId);
data.put("dealSeq", dealSeq);
final Map<String, Object> whiteCardData = new HashMap<>();
// same re: more custom deck sources
whiteCardData.put("isCustom", !(card instanceof PyxWhiteCard));
whiteCardData.put("isWriteIn", card.isWriteIn());
// negative IDs would be custom: either blank or cardcast. they are not stable.
whiteCardData.put("id", card.getId());
whiteCardData.put("watermark", card.getWatermark());
whiteCardData.put("text", card.getText());
data.put("card", whiteCardData);
send(getEventMap("cardDealt", data));
}
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2017, Andy Janata * Copyright (c) 2017-2018, Andy Janata
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted * Redistribution and use in source and binary forms, with or without modification, are permitted
@ -29,12 +29,12 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.maxmind.geoip2.model.CityResponse;
import net.socialgamer.cah.data.BlackCard; import net.socialgamer.cah.data.BlackCard;
import net.socialgamer.cah.data.CardSet; import net.socialgamer.cah.data.CardSet;
import net.socialgamer.cah.data.WhiteCard; import net.socialgamer.cah.data.WhiteCard;
import com.maxmind.geoip2.model.CityResponse;
/** /**
* Collect metrics about card plays, and correlate them with (anonymized) user data. * Collect metrics about card plays, and correlate them with (anonymized) user data.
@ -58,4 +58,6 @@ public interface Metrics {
void gameStart(String gameId, Collection<CardSet> decks, int blanks, int maxPlayers, void gameStart(String gameId, Collection<CardSet> decks, int blanks, int maxPlayers,
int scoreGoal, boolean hasPassword); int scoreGoal, boolean hasPassword);
void cardDealt(String gameId, String sessionId, WhiteCard card, long dealSeq);
} }

View File

@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2017, Andy Janata * Copyright (c) 2017-2018, Andy Janata
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted * Redistribution and use in source and binary forms, with or without modification, are permitted
@ -27,15 +27,15 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.socialgamer.cah.data.BlackCard;
import net.socialgamer.cah.data.CardSet;
import net.socialgamer.cah.data.WhiteCard;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.model.CityResponse;
import net.socialgamer.cah.data.BlackCard;
import net.socialgamer.cah.data.CardSet;
import net.socialgamer.cah.data.WhiteCard;
/** /**
* A no-op metrics implementation. All data are logged at TRACE then discarded. * A no-op metrics implementation. All data are logged at TRACE then discarded.
@ -84,4 +84,10 @@ public class NoOpMetrics implements Metrics {
LOG.trace(String.format("roundJudged(%s, %s, %s, %s, %s, %s)", gameId, roundId, judgeSessionId, LOG.trace(String.format("roundJudged(%s, %s, %s, %s, %s, %s)", gameId, roundId, judgeSessionId,
winnerSessionId, blackCard, cards)); winnerSessionId, blackCard, cards));
} }
@Override
public void cardDealt(final String gameId, final String sessionId, final WhiteCard card,
final long dealSeq) {
LOG.trace(String.format("cardDealt(%s, %s, %s, %d)", gameId, sessionId, card, dealSeq));
}
} }