/* Copyright 2013 Google, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.arbeitspferde.groningen.eventlog; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.google.inject.name.Named; import org.arbeitspferde.groningen.config.GroningenConfig; import org.arbeitspferde.groningen.experimentdb.Experiment; import org.arbeitspferde.groningen.experimentdb.PauseTime; import org.arbeitspferde.groningen.experimentdb.ResourceMetric; import org.arbeitspferde.groningen.experimentdb.SubjectStateBridge; import org.arbeitspferde.groningen.proto.Event; import org.arbeitspferde.groningen.proto.Event.EventEntry; import org.arbeitspferde.groningen.proto.Event.EventEntry.Builder; import org.arbeitspferde.groningen.proto.Event.EventEntry.FitnessScore; import org.arbeitspferde.groningen.proto.Params.GroningenParamsOrBuilder; import org.arbeitspferde.groningen.utility.Clock; import org.arbeitspferde.groningen.utility.MetricExporter; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * A logger that records stats on the performance of a given subject. * * Protocol buffer based logs are written to the supplied {@link EventLoggerService} for deeper * analysis and trending. */ public class SubjectEventProtoLogger implements SubjectEventLogger { /** Logger for this class */ private static final Logger logger = Logger.getLogger(SubjectEventProtoLogger.class.getCanonicalName()); private final Clock clock; /** The event logging service to which to submit the composed events. */ private final EventLoggerService eventLoggerService; /** The groningen server address (added to log events). */ private final String groningenServingAddress; /** The beginning time of this experiment iteration. */ private final long startTime; private final MetricExporter metricExporter; @Inject public SubjectEventProtoLogger(Clock clock, final EventLoggerService eventLoggerService, @Named("servingAddress") final String groningenServingAddress, @Named("startTime") final Long startTime, MetricExporter metricExporter) { this.clock = clock; this.eventLoggerService = eventLoggerService; this.groningenServingAddress = groningenServingAddress; this.startTime = startTime; this.metricExporter = metricExporter; } /** * * @see SubjectEventLogger#logSubjectInExperiment(GroningenConfig, Experiment, * SubjectStateBridge) */ @Override public void logSubjectInExperiment( GroningenConfig config, Experiment experiment, SubjectStateBridge subject) { final SafeProtoLogger<EventEntry> safeProtoLogger = eventLoggerService.getLogger(); Preconditions.checkNotNull(safeProtoLogger, "safeProtoLogger == null"); long experimentId = experiment.getIdOfObject(); final Event.EventEntry event = composeEvent(config, experimentId, subject); try { safeProtoLogger.logProtoEntry(event); } catch (final IOException e) { logger.log(Level.SEVERE, "Could not log event.", e); } catch (final IllegalArgumentException e) { logger.log(Level.SEVERE, "Error encoding the event emission.", e); } } /** * Compose an Event to be logged from a given @{SubjectStateBridge}. * * @param config the active groningen config for this experiment iteration. * @param experimentId the experiment iteration number. * @param subject the subject for which to create the @{EventEntry}. * @return a protocol buffer log entry summarizing the subject's performance. */ @VisibleForTesting EventEntry composeEvent(GroningenConfig config, long experimentId, SubjectStateBridge subject) { final PauseTime pauseTime = subject.getPauseTime(); final ResourceMetric resourceMetric = subject.getResourceMetric(); final GroningenParamsOrBuilder operatingParameters = config.getParamBlock(); final double latencyWeight = operatingParameters.getLatencyWeight(); final double throughputWeight = operatingParameters.getThroughputWeight(); final double memoryWeight = operatingParameters.getMemoryWeight(); final double latencyScore = pauseTime.computeScore(PauseTime.ScoreType.LATENCY); final double throughputScore = pauseTime.computeScore(PauseTime.ScoreType.THROUGHPUT); final double memoryScore = resourceMetric.computeScore(ResourceMetric.ScoreType.MEMORY); final String processServingAddress = subject.getAssociatedSubject().getServingAddress(); // TODO(team): This is a gross, over-broad categorization. final Event.EventEntry.Type result = subject.isInvalid() == Boolean.FALSE ? Event.EventEntry.Type.EXPERIMENT_END : Event.EventEntry.Type.UNEXPECTED_DEATH; final Builder eventBuilder = Event.EventEntry.newBuilder() .setSubjectServingAddress(processServingAddress) .setGroningenServingAddress(groningenServingAddress) .setExperimentId(experimentId) .setType(result) // We may want to get this from the state machine directly instead of deriving here. .setTime(clock.now().getMillis()) .setGroningenStartTime(startTime); // TODO(team): Re-evaluate protocol buffer, and just fundamental data types to avoid casts. final FitnessScore latencyScoreType = FitnessScore.newBuilder() .setName("LATENCY") .setCoefficient((float) latencyWeight) .setScore((float) latencyScore) .build(); eventBuilder.addScore(latencyScoreType); final FitnessScore throughputScoreType = FitnessScore.newBuilder() .setName("THROUGHPUT") .setCoefficient((float) throughputWeight) .setScore((float) throughputScore) .build(); eventBuilder.addScore(throughputScoreType); final FitnessScore memoryScoreType = FitnessScore.newBuilder() .setName("MEMORY") .setCoefficient((float) memoryWeight) .setScore((float) memoryScore) .build(); eventBuilder.addScore(memoryScoreType); // TODO(team): Handle command line arguments. eventBuilder.setGroningenConfiguration(config.getProtoConfig()); for (final double pauseTimeDuration : subject.getPauseTime().getPauseDurations()) { eventBuilder.addPauseEventBuilder().setDurationInSeconds(pauseTimeDuration).build(); } return eventBuilder.build(); } }