Home | History | Annotate | Download | only in runner
      1 /*
      2  * Copyright (C) 2011 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.caliper.runner;
     18 
     19 import static java.util.logging.Level.SEVERE;
     20 
     21 import com.google.caliper.api.ResultProcessor;
     22 import com.google.caliper.config.CaliperConfig;
     23 import com.google.caliper.config.InvalidConfigurationException;
     24 import com.google.caliper.config.ResultProcessorConfig;
     25 import com.google.caliper.model.Run;
     26 import com.google.caliper.model.Trial;
     27 import com.google.caliper.options.CaliperDirectory;
     28 import com.google.common.base.Charsets;
     29 import com.google.common.base.Optional;
     30 import com.google.common.io.Files;
     31 import com.google.gson.Gson;
     32 import com.google.gson.stream.JsonWriter;
     33 
     34 import org.joda.time.format.ISODateTimeFormat;
     35 
     36 import java.io.File;
     37 import java.io.FileOutputStream;
     38 import java.io.IOException;
     39 import java.io.OutputStreamWriter;
     40 import java.util.logging.Logger;
     41 
     42 import javax.inject.Inject;
     43 
     44 /**
     45  * {@link ResultProcessor} that dumps the output data to a file in JSON format. By default, the
     46  * output will be dumped to a file called
     47  * {@code ~/.caliper/results/[benchmark classname].[timestamp].json}; if it exists and is a file,
     48  * the file will be overwritten.  The location can be overridden as either a file or a directory
     49  * using either the {@code file} or {@code dir} options respectively.
     50  */
     51 final class OutputFileDumper implements ResultProcessor {
     52   private static final Logger logger = Logger.getLogger(OutputFileDumper.class.getName());
     53 
     54   private final Run run;
     55   private final Gson gson;
     56   private final File resultFile;
     57   private final File workFile;
     58 
     59   private Optional<JsonWriter> writer = Optional.absent();
     60 
     61   @Inject OutputFileDumper(Run run,
     62       BenchmarkClass benchmarkClass,
     63       Gson gson,
     64       CaliperConfig caliperConfig,
     65       @CaliperDirectory File caliperDirectory) throws InvalidConfigurationException {
     66     this.run = run;
     67     ResultProcessorConfig config = caliperConfig.getResultProcessorConfig(OutputFileDumper.class);
     68     if (config.options().containsKey("file")) {
     69       this.resultFile = new File(config.options().get("file"));
     70       logger.finer("found an output file in the configuration");
     71     } else if (config.options().containsKey("dir")) {
     72       File dir = new File(config.options().get("dir"));
     73       if (dir.isFile()) {
     74         throw new InvalidConfigurationException("specified a directory, but it's a file");
     75       }
     76       this.resultFile = new File(dir, createFileName(benchmarkClass.name()));
     77       logger.finer("found an output directory in the configuration");
     78     } else {
     79       this.resultFile =
     80           new File(new File(caliperDirectory, "results"), createFileName(benchmarkClass.name()));
     81       logger.fine("found no configuration");
     82     }
     83     logger.fine(String.format("using %s for results", resultFile));
     84     this.gson = gson;
     85     this.workFile = new File(resultFile.getPath() + ".tmp");
     86   }
     87 
     88   private String createFileName(String benchmarkName) {
     89     return String.format("%s.%s.json", benchmarkName, createTimestamp());
     90   }
     91 
     92   private String createTimestamp() {
     93     return ISODateTimeFormat.dateTimeNoMillis().print(run.startTime());
     94   }
     95 
     96   @Override public void processTrial(Trial trial) {
     97     if (!writer.isPresent()) {
     98       try {
     99         Files.createParentDirs(workFile);
    100         JsonWriter writer =
    101             new JsonWriter(new OutputStreamWriter(new FileOutputStream(workFile), Charsets.UTF_8));
    102         writer.setIndent("  ");  // always pretty print
    103         writer.beginArray();
    104         this.writer = Optional.of(writer);
    105       } catch (IOException e) {
    106         logger.log(SEVERE, String.format(
    107             "An error occured writing trial %s. Results in %s will be incomplete.", trial.id(),
    108             resultFile), e);
    109       }
    110     }
    111     if (writer.isPresent()) {
    112       gson.toJson(trial, Trial.class, writer.get());
    113     }
    114   }
    115 
    116   @Override public void close() throws IOException {
    117     if (writer.isPresent()) {
    118       writer.get().endArray().close();
    119     }
    120     if (workFile.exists()) {
    121       Files.move(workFile, resultFile);
    122     }
    123   }
    124 }
    125