From 946dfa489606147a9b77923715540eb83af054ef Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Mon, 28 Sep 2015 17:31:25 +0200 Subject: [PATCH] Merge with pro --- .../gatling/app/CoreComponentsFactory.scala | 72 +++++-------------- .../main/scala/io/gatling/app/Gatling.scala | 15 ++-- ...ocessor.scala => RunResultProcessor.scala} | 16 ++--- .../report/AllSessionsReportGenerator.scala | 4 +- .../report/AssertionsReportGenerator.scala | 4 +- .../charts/report/GlobalReportGenerator.scala | 24 +++---- .../report/GroupDetailsReportGenerator.scala | 16 ++--- .../report/ReportsGenerationInputs.scala | 4 +- .../charts/report/ReportsGenerator.scala | 4 +- .../RequestDetailsReportGenerator.scala | 18 ++--- .../charts/report/StatsReportGenerator.scala | 20 +++--- ...leDataReader.scala => LogFileReader.scala} | 10 +-- .../stats/buffers/GeneralStatsBuffers.scala | 4 +- .../charts/template/ConsoleTemplate.scala | 9 ++- ...aderSpec.scala => LogFileReaderSpec.scala} | 48 ++++++------- .../scala/io/gatling/core/ConfigKeys.scala | 6 ++ .../core/config/GatlingConfiguration.scala | 14 ++++ .../io/gatling/core/stats/StatsEngine.scala | 36 +++++++++- .../core/stats/writer/DataWriterType.scala | 2 +- ...taWriter.scala => LogFileDataWriter.scala} | 6 +- .../core/action/ExitHereIfFailedSpec.scala | 4 +- .../io/gatling/core/action/FeedSpec.scala | 4 +- .../io/gatling/core/action/GroupEndSpec.scala | 4 +- .../gatling/core/action/GroupStartSpec.scala | 9 ++- .../scala/io/gatling/core/action/IfSpec.scala | 4 +- ...Spec.scala => LogFileDataWriterSpec.scala} | 4 +- .../io/gatling/http/ahc/AhcFactory.scala | 15 ++-- .../async/polling/PollerActorSpec.scala | 4 +- 28 files changed, 197 insertions(+), 183 deletions(-) rename gatling-app/src/main/scala/io/gatling/app/{ResultsProcessor.scala => RunResultProcessor.scala} (83%) rename gatling-charts/src/main/scala/io/gatling/charts/stats/{FileDataReader.scala => LogFileReader.scala} (97%) rename gatling-charts/src/test/scala/io/gatling/charts/result/reader/{FileDataReaderSpec.scala => LogFileReaderSpec.scala} (73%) rename gatling-core/src/main/scala/io/gatling/core/stats/writer/{FileDataWriter.scala => LogFileDataWriter.scala} (98%) rename gatling-core/src/test/scala/io/gatling/core/stats/writer/{FileDataWriterSpec.scala => LogFileDataWriterSpec.scala} (96%) diff --git a/gatling-app/src/main/scala/io/gatling/app/CoreComponentsFactory.scala b/gatling-app/src/main/scala/io/gatling/app/CoreComponentsFactory.scala index 612979a49c..d39c1227f8 100644 --- a/gatling-app/src/main/scala/io/gatling/app/CoreComponentsFactory.scala +++ b/gatling-app/src/main/scala/io/gatling/app/CoreComponentsFactory.scala @@ -15,76 +15,41 @@ */ package io.gatling.app -import scala.concurrent.{ Future, Await } -import scala.concurrent.duration._ -import scala.util.{ Failure, Success } - -import io.gatling.commons.util.ReflectionHelper._ import io.gatling.core.CoreComponents import io.gatling.core.action.Exit import io.gatling.core.config.GatlingConfiguration import io.gatling.core.controller.Controller import io.gatling.core.controller.throttle.Throttler import io.gatling.core.scenario.SimulationParams -import io.gatling.core.stats.{ DefaultStatsEngine, StatsEngine } -import io.gatling.core.stats.writer.{ Init, ShortScenarioDescription, RunMessage } +import io.gatling.core.stats.DataWritersStatsEngine +import io.gatling.core.stats.writer.RunMessage -import akka.actor.{ Props, Actor, ActorSystem } -import akka.pattern._ -import akka.util.Timeout +import akka.actor.ActorSystem private[gatling] object CoreComponentsFactory { - val CoreComponentsFactorySystemProperty = "gatling.coreComponentsFactory" - - def apply(configuration: GatlingConfiguration): CoreComponentsFactory = - sys.props.get(CoreComponentsFactorySystemProperty).map(newInstance[CoreComponentsFactory]) - .getOrElse(new DefaultCoreComponentsFactory) + def apply(configuration: GatlingConfiguration): CoreComponentsFactory = { + // + // + // + // + // + // + new DefaultCoreComponentsFactory()(configuration) + } } private[gatling] trait CoreComponentsFactory { - def coreComponents(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage)(implicit configuration: GatlingConfiguration): CoreComponents + def coreComponents(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage): CoreComponents - def resultsProcessor(implicit configuration: GatlingConfiguration): ResultsProcessor + def runResultProcessor: RunResultProcessor } -private[gatling] class DefaultCoreComponentsFactory extends CoreComponentsFactory { - - private def newStatsEngine(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage)(implicit configuration: GatlingConfiguration): StatsEngine = { - - implicit val dataWriterTimeOut = Timeout(5 seconds) - - val dataWriters = configuration.data.dataWriters.map { dw => - val clazz = Class.forName(dw.className).asInstanceOf[Class[Actor]] - system.actorOf(Props(clazz), clazz.getName) - } - - val shortScenarioDescriptions = simulationParams.populationBuilders.map(pb => ShortScenarioDescription(pb.scenarioBuilder.name, pb.injectionProfile.userCount)) - - val responses = dataWriters.map(_ ? Init(configuration, simulationParams.assertions, runMessage, shortScenarioDescriptions)) - - def allSucceeded(responses: Seq[Any]): Boolean = - responses.map { - case b: Boolean => b - case _ => false - }.forall(identity) - - implicit val dispatcher = system.dispatcher - - val statsEngineF = Future.sequence(responses) - .map(allSucceeded) - .map { - case true => Success(new DefaultStatsEngine(system, dataWriters)) - case false => Failure(new Exception("DataWriters didn't initialize properly")) - } - - Await.result(statsEngineF, 5 seconds).get - } - - def coreComponents(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage)(implicit configuration: GatlingConfiguration): CoreComponents = { - val statsEngine = newStatsEngine(system, simulationParams, runMessage) +private[gatling] class DefaultCoreComponentsFactory(implicit configuration: GatlingConfiguration) extends CoreComponentsFactory { + def coreComponents(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage): CoreComponents = { + val statsEngine = DataWritersStatsEngine(system, simulationParams, runMessage, configuration) val throttler = Throttler(system, simulationParams) val controller = system.actorOf(Controller.props(statsEngine, throttler, simulationParams, configuration), Controller.ControllerActorName) val exit = system.actorOf(Exit.props(controller, statsEngine), Exit.ExitActorName) @@ -92,5 +57,6 @@ private[gatling] class DefaultCoreComponentsFactory extends CoreComponentsFactor CoreComponents(controller, throttler, statsEngine, exit) } - def resultsProcessor(implicit configuration: GatlingConfiguration): ResultsProcessor = new DefaultResultsProcessor() + def runResultProcessor: RunResultProcessor = + new LogFileProcessor } diff --git a/gatling-app/src/main/scala/io/gatling/app/Gatling.scala b/gatling-app/src/main/scala/io/gatling/app/Gatling.scala index bee2751efa..3a139e1bd6 100644 --- a/gatling-app/src/main/scala/io/gatling/app/Gatling.scala +++ b/gatling-app/src/main/scala/io/gatling/app/Gatling.scala @@ -54,26 +54,23 @@ object Gatling { private[app] class Gatling(selectedSimulationClass: SelectedSimulationClass)(implicit configuration: GatlingConfiguration) { - val coreComponentsFactory = CoreComponentsFactory(configuration) - def start: StatusCode = { - StringHelper.checkSupportedJavaVersion() - - val runResult = runIfNecessary - coreComponentsFactory.resultsProcessor.processResults(runResult) + val coreComponentsFactory = CoreComponentsFactory(configuration) + val runResult = runIfNecessary(coreComponentsFactory) + coreComponentsFactory.runResultProcessor.processRunResult(runResult) } - private def runIfNecessary: RunResult = + private def runIfNecessary(coreComponentsFactory: CoreComponentsFactory): RunResult = configuration.core.directory.reportsOnly match { case Some(reportsOnly) => RunResult(reportsOnly, hasAssertions = true) case _ => if (configuration.http.enableGA) Ga.send(configuration.core.version) // -- Run Gatling -- // - run(Selection(selectedSimulationClass)) + run(Selection(selectedSimulationClass), coreComponentsFactory) } - private def run(selection: Selection): RunResult = { + private def run(selection: Selection, coreComponentsFactory: CoreComponentsFactory): RunResult = { // start actor system before creating simulation instance, some components might need it (e.g. shutdown hook) val system = ActorSystem("GatlingSystem", GatlingConfiguration.loadActorSystemConfiguration()) diff --git a/gatling-app/src/main/scala/io/gatling/app/ResultsProcessor.scala b/gatling-app/src/main/scala/io/gatling/app/RunResultProcessor.scala similarity index 83% rename from gatling-app/src/main/scala/io/gatling/app/ResultsProcessor.scala rename to gatling-app/src/main/scala/io/gatling/app/RunResultProcessor.scala index 9e0fbab89b..47e94defc2 100644 --- a/gatling-app/src/main/scala/io/gatling/app/ResultsProcessor.scala +++ b/gatling-app/src/main/scala/io/gatling/app/RunResultProcessor.scala @@ -19,21 +19,21 @@ import java.lang.System.currentTimeMillis import io.gatling.app.cli.StatusCode import io.gatling.charts.report.{ ReportsGenerator, ReportsGenerationInputs } -import io.gatling.charts.stats.FileDataReader +import io.gatling.charts.stats.LogFileReader import io.gatling.commons.stats.assertion.{ AssertionValidator, AssertionResult } import io.gatling.core.config.GatlingConfiguration -trait ResultsProcessor { +trait RunResultProcessor { - def processResults(runResult: RunResult): StatusCode + def processRunResult(runResult: RunResult): StatusCode } -class DefaultResultsProcessor(implicit configuration: GatlingConfiguration) extends ResultsProcessor { +class LogFileProcessor(implicit configuration: GatlingConfiguration) extends RunResultProcessor { - override def processResults(runResult: RunResult): StatusCode = { + override def processRunResult(runResult: RunResult): StatusCode = { val start = currentTimeMillis - initDataReader(runResult) match { + initLogFileReader(runResult) match { case Some(reader) => val assertionResults = AssertionValidator.validateAssertions(reader) @@ -49,9 +49,9 @@ class DefaultResultsProcessor(implicit configuration: GatlingConfiguration) exte } } - private def initDataReader(runResult: RunResult): Option[FileDataReader] = + private def initLogFileReader(runResult: RunResult): Option[LogFileReader] = if (reportsGenerationEnabled || runResult.hasAssertions) - Some(new FileDataReader(runResult.runId)) + Some(new LogFileReader(runResult.runId)) else None diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/AllSessionsReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/AllSessionsReportGenerator.scala index 849d614c14..f84a9df02b 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/AllSessionsReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/AllSessionsReportGenerator.scala @@ -27,9 +27,9 @@ private[charts] class AllSessionsReportGenerator(reportsGenerationInputs: Report def generate(): Unit = { import reportsGenerationInputs._ - val series = new Series[IntVsTimePlot]("All Users", dataReader.numberOfActiveSessionsPerSecond(None), List(Orange)) + val series = new Series[IntVsTimePlot]("All Users", logFileReader.numberOfActiveSessionsPerSecond(None), List(Orange)) - val javascript = componentLibrary.getAllUsersJs(dataReader.runStart, series) + val javascript = componentLibrary.getAllUsersJs(logFileReader.runStart, series) new TemplateWriter(allSessionsFile(reportFolderName)).writeToFile(javascript) } diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/AssertionsReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/AssertionsReportGenerator.scala index 25244d5931..a1922eb61f 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/AssertionsReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/AssertionsReportGenerator.scala @@ -25,7 +25,7 @@ private[charts] class AssertionsReportGenerator(reportsGenerationInputs: Reports import reportsGenerationInputs._ def generate(): Unit = { - new TemplateWriter(assertionsJUnitFile(reportFolderName)).writeToFile(new AssertionsJUnitTemplate(dataReader.runMessage, assertionResults).getOutput) - new TemplateWriter(assertionsJsonFile(reportFolderName)).writeToFile(new AssertionsJsonTemplate(dataReader.runMessage, dataReader.scenarioNames, assertionResults).getOutput) + new TemplateWriter(assertionsJUnitFile(reportFolderName)).writeToFile(new AssertionsJUnitTemplate(logFileReader.runMessage, assertionResults).getOutput) + new TemplateWriter(assertionsJsonFile(reportFolderName)).writeToFile(new AssertionsJsonTemplate(logFileReader.runMessage, logFileReader.scenarioNames, assertionResults).getOutput) } } diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/GlobalReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/GlobalReportGenerator.scala index eaa5f67b9b..0c279fc0ce 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/GlobalReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/GlobalReportGenerator.scala @@ -32,20 +32,20 @@ private[charts] class GlobalReportGenerator(reportsGenerationInputs: ReportsGene def activeSessionsChartComponent = { val baseColors = List(Blue, Green, Red, Yellow, Cyan, Lime, Purple, Pink, LightBlue, LightOrange, LightRed, LightLime, LightPurple, LightPink) - val seriesColors = Iterator.continually(baseColors).flatten.take(dataReader.scenarioNames.size).toList + val seriesColors = Iterator.continually(baseColors).flatten.take(logFileReader.scenarioNames.size).toList - val activeSessionsSeries: Seq[Series[IntVsTimePlot]] = dataReader + val activeSessionsSeries: Seq[Series[IntVsTimePlot]] = logFileReader .scenarioNames - .map { scenarioName => scenarioName -> dataReader.numberOfActiveSessionsPerSecond(Some(scenarioName)) } + .map { scenarioName => scenarioName -> logFileReader.numberOfActiveSessionsPerSecond(Some(scenarioName)) } .reverse .zip(seriesColors) .map { case ((scenarioName, data), color) => new Series[IntVsTimePlot](scenarioName, data, List(color)) } - componentLibrary.getActiveSessionsChartComponent(dataReader.runStart, activeSessionsSeries) + componentLibrary.getActiveSessionsChartComponent(logFileReader.runStart, activeSessionsSeries) } def responseTimeDistributionChartComponent: Component = { - val (okDistribution, koDistribution) = dataReader.responseTimeDistribution(100, None, None) + val (okDistribution, koDistribution) = logFileReader.responseTimeDistribution(100, None, None) val okDistributionSeries = new Series(Series.OK, okDistribution, List(Blue)) val koDistributionSeries = new Series(Series.KO, koDistribution, List(Red)) @@ -53,7 +53,7 @@ private[charts] class GlobalReportGenerator(reportsGenerationInputs: ReportsGene } def responseTimeChartComponent: Component = - percentilesChartComponent(dataReader.responseTimePercentilesOverTime, componentLibrary.getRequestDetailsResponseTimeChartComponent, "Response Time Percentiles over Time") + percentilesChartComponent(logFileReader.responseTimePercentilesOverTime, componentLibrary.getRequestDetailsResponseTimeChartComponent, "Response Time Percentiles over Time") def percentilesChartComponent( dataSource: (Status, Option[String], Option[Group]) => Iterable[PercentilesVsTimePlot], @@ -63,14 +63,14 @@ private[charts] class GlobalReportGenerator(reportsGenerationInputs: ReportsGene val successData = dataSource(OK, None, None) val successSeries = new Series[PercentilesVsTimePlot](s"$title (${Series.OK})", successData, ReportGenerator.PercentilesColors) - componentFactory(dataReader.runStart, successSeries) + componentFactory(logFileReader.runStart, successSeries) } def requestsChartComponent: Component = - countsChartComponent(dataReader.numberOfRequestsPerSecond, componentLibrary.getRequestsChartComponent) + countsChartComponent(logFileReader.numberOfRequestsPerSecond, componentLibrary.getRequestsChartComponent) def responsesChartComponent: Component = - countsChartComponent(dataReader.numberOfResponsesPerSecond, componentLibrary.getResponsesChartComponent) + countsChartComponent(logFileReader.numberOfResponsesPerSecond, componentLibrary.getResponsesChartComponent) def countsChartComponent( dataSource: (Option[String], Option[Group]) => Seq[CountsVsTimePlot], @@ -83,15 +83,15 @@ private[charts] class GlobalReportGenerator(reportsGenerationInputs: ReportsGene val koPieSlice = PieSlice(Series.KO, count(counts, KO)) val pieRequestsSeries = new Series[PieSlice](Series.Distribution, Seq(okPieSlice, koPieSlice), List(Green, Red)) - componentFactory(dataReader.runStart, countsSeries, pieRequestsSeries) + componentFactory(logFileReader.runStart, countsSeries, pieRequestsSeries) } val template = new GlobalPageTemplate( - componentLibrary.getNumberOfRequestsChartComponent(dataReader.requestNames.size), + componentLibrary.getNumberOfRequestsChartComponent(logFileReader.requestNames.size), componentLibrary.getRequestDetailsIndicatorChartComponent, new AssertionsTableComponent(assertionResults), new StatisticsTableComponent, - new ErrorsTableComponent(dataReader.errors(None, None)), + new ErrorsTableComponent(logFileReader.errors(None, None)), activeSessionsChartComponent, responseTimeDistributionChartComponent, responseTimeChartComponent, diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/GroupDetailsReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/GroupDetailsReportGenerator.scala index fb86bb36b6..55923ce0af 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/GroupDetailsReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/GroupDetailsReportGenerator.scala @@ -32,14 +32,14 @@ private[charts] class GroupDetailsReportGenerator(reportsGenerationInputs: Repor def generateDetailPage(path: String, group: Group): Unit = { def cumulatedResponseTimeChartComponent: Component = { - val dataSuccess = dataReader.groupCumulatedResponseTimePercentilesOverTime(OK, group) + val dataSuccess = logFileReader.groupCumulatedResponseTimePercentilesOverTime(OK, group) val seriesSuccess = new Series[PercentilesVsTimePlot]("Group Cumulated Response Time Percentiles over Time (success)", dataSuccess, ReportGenerator.PercentilesColors) - componentLibrary.getGroupDetailsDurationChartComponent("cumulatedResponseTimeChartContainer", "Cumulated Response Time (ms)", dataReader.runStart, seriesSuccess) + componentLibrary.getGroupDetailsDurationChartComponent("cumulatedResponseTimeChartContainer", "Cumulated Response Time (ms)", logFileReader.runStart, seriesSuccess) } def cumulatedResponseTimeDistributionChartComponent: Component = { - val (distributionSuccess, distributionFailure) = dataReader.groupCumulatedResponseTimeDistribution(100, group) + val (distributionSuccess, distributionFailure) = logFileReader.groupCumulatedResponseTimeDistribution(100, group) val distributionSeriesSuccess = new Series("Group cumulated response time (success)", distributionSuccess, List(Blue)) val distributionSeriesFailure = new Series("Group cumulated response time (failure)", distributionFailure, List(Red)) @@ -47,14 +47,14 @@ private[charts] class GroupDetailsReportGenerator(reportsGenerationInputs: Repor } def durationChartComponent: Component = { - val dataSuccess = dataReader.groupDurationPercentilesOverTime(OK, group) + val dataSuccess = logFileReader.groupDurationPercentilesOverTime(OK, group) val seriesSuccess = new Series[PercentilesVsTimePlot]("Group Duration Percentiles over Time (success)", dataSuccess, ReportGenerator.PercentilesColors) - componentLibrary.getGroupDetailsDurationChartComponent("durationContainer", "Duration (ms)", dataReader.runStart, seriesSuccess) + componentLibrary.getGroupDetailsDurationChartComponent("durationContainer", "Duration (ms)", logFileReader.runStart, seriesSuccess) } def durationDistributionChartComponent: Component = { - val (distributionSuccess, distributionFailure) = dataReader.groupDurationDistribution(100, group) + val (distributionSuccess, distributionFailure) = logFileReader.groupDurationDistribution(100, group) val distributionSeriesSuccess = new Series("Group duration (success)", distributionSuccess, List(Blue)) val distributionSeriesFailure = new Series("Group duration (failure)", distributionFailure, List(Red)) @@ -69,7 +69,7 @@ private[charts] class GroupDetailsReportGenerator(reportsGenerationInputs: Repor group, statisticsComponent, indicatorChartComponent, - new ErrorsTableComponent(dataReader.errors(None, Some(group))), + new ErrorsTableComponent(logFileReader.errors(None, Some(group))), cumulatedResponseTimeChartComponent, cumulatedResponseTimeDistributionChartComponent, durationChartComponent, @@ -79,7 +79,7 @@ private[charts] class GroupDetailsReportGenerator(reportsGenerationInputs: Repor new TemplateWriter(groupFile(reportFolderName, path)).writeToFile(template.getOutput(configuration.core.charset)) } - dataReader.statsPaths.foreach { + logFileReader.statsPaths.foreach { case GroupStatsPath(group) => generateDetailPage(RequestPath.path(group), group) case _ => } diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerationInputs.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerationInputs.scala index d337c8ddbb..52ccfd455a 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerationInputs.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerationInputs.scala @@ -15,11 +15,11 @@ */ package io.gatling.charts.report -import io.gatling.charts.stats.FileDataReader +import io.gatling.charts.stats.LogFileReader import io.gatling.commons.stats.assertion.AssertionResult private[gatling] case class ReportsGenerationInputs( reportFolderName: String, - dataReader: FileDataReader, + logFileReader: LogFileReader, assertionResults: List[AssertionResult] ) diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerator.scala index 128441a701..d36a0af4db 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/ReportsGenerator.scala @@ -31,7 +31,7 @@ private[gatling] class ReportsGenerator(implicit configuration: GatlingConfigura import reportsGenerationInputs._ def hasAtLeastOneRequestReported: Boolean = - dataReader.statsPaths.exists(_.isInstanceOf[RequestStatsPath]) + logFileReader.statsPaths.exists(_.isInstanceOf[RequestStatsPath]) def generateMenu(): Unit = new TemplateWriter(menuFile(reportFolderName)).writeToFile(new MenuTemplate().getOutput) @@ -57,7 +57,7 @@ private[gatling] class ReportsGenerator(implicit configuration: GatlingConfigura copyAssets() generateMenu() - PageTemplate.setRunInfo(dataReader.runMessage, dataReader.runEnd) + PageTemplate.setRunInfo(logFileReader.runMessage, logFileReader.runEnd) reportGenerators.foreach(_.generate()) generateStats() generateAssertions() diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/RequestDetailsReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/RequestDetailsReportGenerator.scala index a9cc9f70d6..1009af3705 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/RequestDetailsReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/RequestDetailsReportGenerator.scala @@ -33,7 +33,7 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep def generateDetailPage(path: String, requestName: String, group: Option[Group]): Unit = { def responseTimeDistributionChartComponent: Component = { - val (okDistribution, koDistribution) = dataReader.responseTimeDistribution(100, Some(requestName), group) + val (okDistribution, koDistribution) = logFileReader.responseTimeDistribution(100, Some(requestName), group) val okDistributionSeries = new Series(Series.OK, okDistribution, List(Blue)) val koDistributionSeries = new Series(Series.KO, koDistribution, List(Red)) @@ -41,7 +41,7 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep } def responseTimeChartComponent: Component = - percentilesChartComponent(dataReader.responseTimePercentilesOverTime, componentLibrary.getRequestDetailsResponseTimeChartComponent, "Response Time Percentiles over Time") + percentilesChartComponent(logFileReader.responseTimePercentilesOverTime, componentLibrary.getRequestDetailsResponseTimeChartComponent, "Response Time Percentiles over Time") def percentilesChartComponent( dataSource: (Status, Option[String], Option[Group]) => Iterable[PercentilesVsTimePlot], @@ -51,14 +51,14 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep val successData = dataSource(OK, Some(requestName), group) val successSeries = new Series[PercentilesVsTimePlot](s"$title (${Series.OK})", successData, ReportGenerator.PercentilesColors) - componentFactory(dataReader.runStart, successSeries) + componentFactory(logFileReader.runStart, successSeries) } def requestsChartComponent: Component = - countsChartComponent(dataReader.numberOfRequestsPerSecond, componentLibrary.getRequestsChartComponent) + countsChartComponent(logFileReader.numberOfRequestsPerSecond, componentLibrary.getRequestsChartComponent) def responsesChartComponent: Component = - countsChartComponent(dataReader.numberOfResponsesPerSecond, componentLibrary.getResponsesChartComponent) + countsChartComponent(logFileReader.numberOfResponsesPerSecond, componentLibrary.getResponsesChartComponent) def countsChartComponent( dataSource: (Option[String], Option[Group]) => Seq[CountsVsTimePlot], @@ -72,11 +72,11 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep val koPieSlice = PieSlice(Series.KO, count(counts, KO)) val pieRequestsSeries = new Series[PieSlice](Series.Distribution, Seq(okPieSlice, koPieSlice), List(Green, Red)) - componentFactory(dataReader.runStart, countsSeries, pieRequestsSeries) + componentFactory(logFileReader.runStart, countsSeries, pieRequestsSeries) } def responseTimeScatterChartComponent: Component = - scatterChartComponent(dataReader.responseTimeAgainstGlobalNumberOfRequestsPerSec, componentLibrary.getRequestDetailsResponseTimeScatterChartComponent) + scatterChartComponent(logFileReader.responseTimeAgainstGlobalNumberOfRequestsPerSec, componentLibrary.getRequestDetailsResponseTimeScatterChartComponent) def scatterChartComponent( dataSource: (Status, String, Option[Group]) => Seq[IntVsTimePlot], @@ -95,7 +95,7 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep new RequestDetailsPageTemplate(path, requestName, group, new StatisticsTextComponent, componentLibrary.getRequestDetailsIndicatorChartComponent, - new ErrorsTableComponent(dataReader.errors(Some(requestName), group)), + new ErrorsTableComponent(logFileReader.errors(Some(requestName), group)), responseTimeDistributionChartComponent, responseTimeChartComponent, requestsChartComponent, @@ -105,7 +105,7 @@ private[charts] class RequestDetailsReportGenerator(reportsGenerationInputs: Rep new TemplateWriter(requestFile(reportFolderName, path)).writeToFile(template.getOutput(configuration.core.charset)) } - dataReader.statsPaths.foreach { + logFileReader.statsPaths.foreach { case RequestStatsPath(request, group) => generateDetailPage(RequestPath.path(request, group), request, group) case _ => } diff --git a/gatling-charts/src/main/scala/io/gatling/charts/report/StatsReportGenerator.scala b/gatling-charts/src/main/scala/io/gatling/charts/report/StatsReportGenerator.scala index cf2dd10002..1f4bf73003 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/report/StatsReportGenerator.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/report/StatsReportGenerator.scala @@ -36,9 +36,9 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener def computeRequestStats(name: String, requestName: Option[String], group: Option[Group]): RequestStatistics = { - val total = dataReader.requestGeneralStats(requestName, group, None) - val ok = dataReader.requestGeneralStats(requestName, group, Some(OK)) - val ko = dataReader.requestGeneralStats(requestName, group, Some(KO)) + val total = logFileReader.requestGeneralStats(requestName, group, None) + val ok = logFileReader.requestGeneralStats(requestName, group, Some(OK)) + val ko = logFileReader.requestGeneralStats(requestName, group, Some(KO)) val numberOfRequestsStatistics = Statistics("request count", total.count, ok.count, ko.count) val minResponseTimeStatistics = Statistics("min response time", total.min, ok.min, ko.min) @@ -54,7 +54,7 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener val percentiles4 = percentiles(configuration.charting.indicators.percentile4, percentilesTitle, total, ok, ko) val meanNumberOfRequestsPerSecondStatistics = Statistics("mean requests/sec", total.meanRequestsPerSec, ok.meanRequestsPerSec, ko.meanRequestsPerSec) - val groupedCounts = dataReader + val groupedCounts = logFileReader .numberOfRequestInResponseTimeRange(requestName, group).map { case (rangeName, count) => GroupedCount(rangeName, count, total.count) } @@ -69,9 +69,9 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener def computeGroupStats(name: String, group: Group): RequestStatistics = { - val total = dataReader.groupCumulatedResponseTimeGeneralStats(group, None) - val ok = dataReader.groupCumulatedResponseTimeGeneralStats(group, Some(OK)) - val ko = dataReader.groupCumulatedResponseTimeGeneralStats(group, Some(KO)) + val total = logFileReader.groupCumulatedResponseTimeGeneralStats(group, None) + val ok = logFileReader.groupCumulatedResponseTimeGeneralStats(group, Some(OK)) + val ko = logFileReader.groupCumulatedResponseTimeGeneralStats(group, Some(KO)) val numberOfRequestsStatistics = Statistics("numberOfRequests", total.count, ok.count, ko.count) val minResponseTimeStatistics = Statistics("minResponseTime", total.min, ok.min, ko.min) @@ -85,7 +85,7 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener val percentiles4 = percentiles(configuration.charting.indicators.percentile4, _ => "percentiles4", total, ok, ko) val meanNumberOfRequestsPerSecondStatistics = Statistics("meanNumberOfRequestsPerSecond", total.meanRequestsPerSec, ok.meanRequestsPerSec, ko.meanRequestsPerSec) - val groupedCounts = dataReader + val groupedCounts = logFileReader .numberOfRequestInResponseTimeRange(None, Some(group)).map { case (rangeName, count) => GroupedCount(rangeName, count, total.count) } @@ -97,7 +97,7 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener val rootContainer = GroupContainer.root(computeRequestStats(GlobalPageName, None, None)) - val statsPaths = dataReader.statsPaths + val statsPaths = logFileReader.statsPaths val groupsByHierarchy: Map[List[String], Group] = statsPaths .collect { @@ -136,6 +136,6 @@ private[charts] class StatsReportGenerator(reportsGenerationInputs: ReportsGener new TemplateWriter(statsJsFile(reportFolderName)).writeToFile(new StatsJsTemplate(rootContainer, false).getOutput(configuration.core.charset)) new TemplateWriter(statsJsonFile(reportFolderName)).writeToFile(new StatsJsTemplate(rootContainer, true).getOutput(configuration.core.charset)) new TemplateWriter(globalStatsJsonFile(reportFolderName)).writeToFile(new GlobalStatsJsonTemplate(rootContainer.stats, true).getOutput) - println(ConsoleTemplate(dataReader, rootContainer.stats)) + println(ConsoleTemplate.println(rootContainer.stats, logFileReader.errors(None, None))) } } diff --git a/gatling-charts/src/main/scala/io/gatling/charts/stats/FileDataReader.scala b/gatling-charts/src/main/scala/io/gatling/charts/stats/LogFileReader.scala similarity index 97% rename from gatling-charts/src/main/scala/io/gatling/charts/stats/FileDataReader.scala rename to gatling-charts/src/main/scala/io/gatling/charts/stats/LogFileReader.scala index 3c6da6bb04..99a8a51f68 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/stats/FileDataReader.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/stats/LogFileReader.scala @@ -35,16 +35,16 @@ import boopickle.Default._ import com.typesafe.scalalogging.StrictLogging import jodd.util.Base64 -object FileDataReader { +object LogFileReader { val LogStep = 100000 val SecMillisecRatio = 1000.0 val SimulationFilesNamePattern = """.*\.log""" } -class FileDataReader(runUuid: String)(implicit configuration: GatlingConfiguration) extends GeneralStatsSource with StrictLogging { +class LogFileReader(runUuid: String)(implicit configuration: GatlingConfiguration) extends GeneralStatsSource with StrictLogging { - import FileDataReader._ + import LogFileReader._ println("Parsing log file(s)...") @@ -87,7 +87,7 @@ class FileDataReader(runUuid: String)(implicit configuration: GatlingConfigurati count += 1 if (count % LogStep == 0) logger.info(s"First pass, read $count lines") - line.split(FileDataWriter.Separator) match { + line.split(LogFileDataWriter.Separator) match { case RawRequestRecord(array) => updateRunLimits(array(5).toLong, array(6).toLong) @@ -146,7 +146,7 @@ class FileDataReader(runUuid: String)(implicit configuration: GatlingConfigurati count += 1 if (count % LogStep == 0) logger.info(s"Second pass, read $count lines") - line.split(FileDataWriter.Separator) match { + line.split(LogFileDataWriter.Separator) match { case requestRecordParser(record) => resultsHolder.addRequestRecord(record) case groupRecordParser(record) => resultsHolder.addGroupRecord(record) case userRecordParser(record) => resultsHolder.addUserRecord(record) diff --git a/gatling-charts/src/main/scala/io/gatling/charts/stats/buffers/GeneralStatsBuffers.scala b/gatling-charts/src/main/scala/io/gatling/charts/stats/buffers/GeneralStatsBuffers.scala index 7377df3b6b..be91645937 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/stats/buffers/GeneralStatsBuffers.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/stats/buffers/GeneralStatsBuffers.scala @@ -17,7 +17,7 @@ package io.gatling.charts.stats.buffers import scala.collection.mutable -import io.gatling.charts.stats.{ GroupRecord, RequestRecord, FileDataReader } +import io.gatling.charts.stats.{ GroupRecord, RequestRecord, LogFileReader } import io.gatling.commons.stats.{ Group, GeneralStats, Status } import io.gatling.core.stats.IntVsTimePlot @@ -86,7 +86,7 @@ private[stats] class GeneralStatsBuffer(duration: Long) { val count = digest.size val mean = (sum / count).toInt val stdDev = math.sqrt((sumOfSquares - (sum * sum) / count) / count).toInt - val meanRequestsPerSec = valuesCount / (duration / FileDataReader.SecMillisecRatio) + val meanRequestsPerSec = valuesCount / (duration / LogFileReader.SecMillisecRatio) val min = digest.quantile(0).toInt val max = digest.quantile(1).toInt diff --git a/gatling-charts/src/main/scala/io/gatling/charts/template/ConsoleTemplate.scala b/gatling-charts/src/main/scala/io/gatling/charts/template/ConsoleTemplate.scala index cd7e140a7c..1666d3dd02 100644 --- a/gatling-charts/src/main/scala/io/gatling/charts/template/ConsoleTemplate.scala +++ b/gatling-charts/src/main/scala/io/gatling/charts/template/ConsoleTemplate.scala @@ -15,10 +15,10 @@ */ package io.gatling.charts.template +import io.gatling.commons.stats.ErrorStats import io.gatling.commons.util.StringHelper._ import io.gatling.charts.component.Statistics import io.gatling.charts.component.Statistics.printable -import io.gatling.charts.stats.FileDataReader import io.gatling.charts.component.{ GroupedCount, RequestStatistics } import io.gatling.core.stats.writer.ConsoleErrorsWriter import io.gatling.core.stats.writer.ConsoleSummary._ @@ -37,8 +37,7 @@ private[charts] object ConsoleTemplate { fast"> ${name.rightPad(OutputLength - 32)} ${count.toString.leftPad(7)} (${percentage.toString.leftPad(3)}%)" } - def writeErrorsAndEndBlock(dataReader: FileDataReader): Fastring = { - val errors = dataReader.errors(None, None) + def writeErrorsAndEndBlock(errors: Seq[ErrorStats]): Fastring = { if (errors.isEmpty) fast"$NewBlock" else @@ -47,7 +46,7 @@ ${errors.map(ConsoleErrorsWriter.writeError).mkFastring(Eol)} $NewBlock""" } - def apply(dataReader: FileDataReader, requestStatistics: RequestStatistics): String = { + def println(requestStatistics: RequestStatistics, errors: Seq[ErrorStats]): String = { import requestStatistics._ fast""" $NewBlock @@ -62,7 +61,7 @@ ${writeRequestCounters(percentiles2)} ${writeRequestCounters(meanNumberOfRequestsPerSecondStatistics)} ${writeSubTitle("Response Time Distribution")} ${groupedCounts.map(writeGroupedCounters).mkFastring(Eol)} -${writeErrorsAndEndBlock(dataReader)} +${writeErrorsAndEndBlock(errors)} """.toString } } diff --git a/gatling-charts/src/test/scala/io/gatling/charts/result/reader/FileDataReaderSpec.scala b/gatling-charts/src/test/scala/io/gatling/charts/result/reader/LogFileReaderSpec.scala similarity index 73% rename from gatling-charts/src/test/scala/io/gatling/charts/result/reader/FileDataReaderSpec.scala rename to gatling-charts/src/test/scala/io/gatling/charts/result/reader/LogFileReaderSpec.scala index 55e1dacd0c..5e2a06948e 100644 --- a/gatling-charts/src/test/scala/io/gatling/charts/result/reader/FileDataReaderSpec.scala +++ b/gatling-charts/src/test/scala/io/gatling/charts/result/reader/LogFileReaderSpec.scala @@ -18,11 +18,11 @@ package io.gatling.charts.result.reader import scala.collection.mutable import io.gatling.BaseSpec -import io.gatling.charts.stats.FileDataReader +import io.gatling.charts.stats.LogFileReader import io.gatling.core.ConfigKeys._ import io.gatling.core.config.{ GatlingConfiguration, GatlingPropertiesBuilder } -class FileDataReaderSpec extends BaseSpec { +class LogFileReaderSpec extends BaseSpec { val props = new GatlingPropertiesBuilder props.sourcesDirectory("src/test/resources") @@ -31,33 +31,33 @@ class FileDataReaderSpec extends BaseSpec { implicit val configuration = GatlingConfiguration.loadForTest(props.build) // FIXME re-enable with fresh and SIMPLE samples - // "When reading a single log file, FileDataReader" should { + // "When reading a single log file, LogFileReader" should { // - // val singleFileDataReader = new FileDataReader("run_single_node") + // val singleLogFileReader = new LogFileReader("run_single_node") // // "be able to read a single file simulation" in { - // singleFileDataReader must not be null + // singleLogFileReader must not be null // } // // "find the two correct scenarios" in { - // singleFileDataReader.scenarioNames must beEqualTo(List("Scenario name", "Other Scenario Name")) + // singleLogFileReader.scenarioNames must beEqualTo(List("Scenario name", "Other Scenario Name")) // } // // "find the fifteen correct requests" in { // val requestNames = List("Request request_1", "Request request_2", "Request request_3", "Request request_4", "Request request_5", "Request request_6", "Request request_7", "Request request_8", "Request request_9", "Request request_10") // val otherRequestNames = List("Request other_request_1", "Request other_request_2", "Request other_request_3", "Request other_request_9", "Request other_request_10") - // singleFileDataReader.groupsAndRequests.collect { case (group, Some(request)) => RequestPath.path(request, group)} must haveTheSameElementsAs(requestNames ++ otherRequestNames) + // singleLogFileReader.groupsAndRequests.collect { case (group, Some(request)) => RequestPath.path(request, group)} must haveTheSameElementsAs(requestNames ++ otherRequestNames) // } // // "have a correct run record" in { - // singleFileDataReader.runMessage must beEqualTo(RunMessage(parseTimestampString("20120607202804"), "run1", "interesting test run")) + // singleLogFileReader.runMessage must beEqualTo(RunMessage(parseTimestampString("20120607202804"), "run1", "interesting test run")) // } // // } // - // "When reading two log files coming from a multinode simulation, FileDataReader" should { + // "When reading two log files coming from a multinode simulation, LogFileReader" should { // - // val multipleFilesDataReader = new FileDataReader("run_multiple_nodes") + // val multipleFilesDataReader = new LogFileReader("run_multiple_nodes") // // "be able to read a multiple files simulation" in { // multipleFilesDataReader must not be null @@ -79,17 +79,17 @@ class FileDataReaderSpec extends BaseSpec { // } // } - val singleFileDataReader = new FileDataReader("run_single_node_with_known_stats") + val singleLogFileReader = new LogFileReader("run_single_node_with_known_stats") "When reading a single log file with known statistics, FileDataReder" should "return expected minResponseTime for correct request data" in { - singleFileDataReader.requestGeneralStats().min shouldBe 2000 + singleLogFileReader.requestGeneralStats().min shouldBe 2000 } it should "return expected maxResponseTime for correct request data" in { - singleFileDataReader.requestGeneralStats().max shouldBe 9000 + singleLogFileReader.requestGeneralStats().max shouldBe 9000 } it should "return expected responseTimeStandardDeviation for correct request data" in { - val computedValue = singleFileDataReader.requestGeneralStats().stdDev + val computedValue = singleLogFileReader.requestGeneralStats().stdDev val expectedValue = 2138 val error = (computedValue.toDouble - expectedValue) / expectedValue @@ -103,9 +103,9 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val lowPercentilesFileDataReader = new FileDataReader("run_single_node_with_known_stats") - lowPercentilesFileDataReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile1) shouldBe 2000 - lowPercentilesFileDataReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile2) shouldBe 5000 + val lowPercentilesLogFileReader = new LogFileReader("run_single_node_with_known_stats") + lowPercentilesLogFileReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile1) shouldBe 2000 + lowPercentilesLogFileReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile2) shouldBe 5000 } it should "return expected result for the (99.99, 100) percentiles" in { @@ -115,9 +115,9 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val highPercentilesFileDataReader = new FileDataReader("run_single_node_with_known_stats") - highPercentilesFileDataReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile1) shouldBe 8860 - highPercentilesFileDataReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile2) shouldBe 9000 + val highPercentilesLogFileReader = new LogFileReader("run_single_node_with_known_stats") + highPercentilesLogFileReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile1) shouldBe 8860 + highPercentilesLogFileReader.requestGeneralStats().percentile(configuration.charting.indicators.percentile2) shouldBe 9000 } it should "indicate that all the request have their response time in between 0 and 100000" in { @@ -127,7 +127,7 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val fileDataReader = new FileDataReader("run_single_node_with_known_stats") + val fileDataReader = new LogFileReader("run_single_node_with_known_stats") fileDataReader.numberOfRequestInResponseTimeRange(None, None).map(_._2) shouldBe List(0, 8, 0, 0) } @@ -138,7 +138,7 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val nRequestInResponseTimeRange = new FileDataReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) + val nRequestInResponseTimeRange = new LogFileReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) nRequestInResponseTimeRange.head shouldBe 1 } @@ -149,7 +149,7 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val nRequestInResponseTimeRange = new FileDataReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) + val nRequestInResponseTimeRange = new LogFileReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) nRequestInResponseTimeRange(1) shouldBe 5 } @@ -160,7 +160,7 @@ class FileDataReaderSpec extends BaseSpec { props.put(core.directory.Simulations, "src/test/resources") props.put(core.directory.Results, "src/test/resources") implicit val configuration = GatlingConfiguration.loadForTest(props) - val nRequestInResponseTimeRange = new FileDataReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) + val nRequestInResponseTimeRange = new LogFileReader("run_single_node_with_known_stats").numberOfRequestInResponseTimeRange(None, None).map(_._2) nRequestInResponseTimeRange(2) shouldBe 2 } } diff --git a/gatling-core/src/main/scala/io/gatling/core/ConfigKeys.scala b/gatling-core/src/main/scala/io/gatling/core/ConfigKeys.scala index 8a335536fe..adf6d1731c 100644 --- a/gatling-core/src/main/scala/io/gatling/core/ConfigKeys.scala +++ b/gatling-core/src/main/scala/io/gatling/core/ConfigKeys.scala @@ -135,4 +135,10 @@ object ConfigKeys { val WriteInterval = "gatling.data.graphite.writeInterval" } } + + // + // + // + // + // } diff --git a/gatling-core/src/main/scala/io/gatling/core/config/GatlingConfiguration.scala b/gatling-core/src/main/scala/io/gatling/core/config/GatlingConfiguration.scala index bbce81dd92..7f0b8736b1 100644 --- a/gatling-core/src/main/scala/io/gatling/core/config/GatlingConfiguration.scala +++ b/gatling-core/src/main/scala/io/gatling/core/config/GatlingConfiguration.scala @@ -220,6 +220,11 @@ object GatlingConfiguration extends StrictLogging { writeInterval = config.getInt(data.graphite.WriteInterval) ) ), +// +// +// +// +// config = config ) @@ -370,10 +375,19 @@ case class GraphiteDataWriterConfiguration( writeInterval: Int ) +// +// +// +// +// + case class GatlingConfiguration( core: CoreConfiguration, charting: ChartingConfiguration, http: HttpConfiguration, data: DataConfiguration, + // + // + // config: Config ) diff --git a/gatling-core/src/main/scala/io/gatling/core/stats/StatsEngine.scala b/gatling-core/src/main/scala/io/gatling/core/stats/StatsEngine.scala index 5c5e2730e9..289272d9e1 100644 --- a/gatling-core/src/main/scala/io/gatling/core/stats/StatsEngine.scala +++ b/gatling-core/src/main/scala/io/gatling/core/stats/StatsEngine.scala @@ -17,17 +17,20 @@ package io.gatling.core.stats import java.util.concurrent.atomic.AtomicBoolean -import scala.concurrent.Future +import scala.concurrent.{ Await, Future } import scala.concurrent.duration._ +import scala.util.{ Failure, Success } import io.gatling.commons.stats.Status import io.gatling.commons.util.TimeHelper._ +import io.gatling.core.config.GatlingConfiguration import io.gatling.core.controller.ControllerCommand +import io.gatling.core.scenario.SimulationParams import io.gatling.core.session.{ GroupBlock, Session } import io.gatling.core.stats.message.ResponseTimings import io.gatling.core.stats.writer._ -import akka.actor.{ ActorRef, ActorSystem } +import akka.actor.{ Props, Actor, ActorRef, ActorSystem } import akka.pattern.ask import akka.util.Timeout @@ -61,7 +64,34 @@ trait StatsEngine { logError(session, requestName, s"Failed to build request $requestName: $errorMessage", nowMillis) } -class DefaultStatsEngine(system: ActorSystem, dataWriters: Seq[ActorRef]) extends StatsEngine { +object DataWritersStatsEngine { + + def apply(system: ActorSystem, simulationParams: SimulationParams, runMessage: RunMessage, configuration: GatlingConfiguration): DataWritersStatsEngine = { + implicit val dataWriterTimeOut = Timeout(5 seconds) + + val dataWriters = configuration.data.dataWriters.map { dw => + val clazz = Class.forName(dw.className).asInstanceOf[Class[Actor]] + system.actorOf(Props(clazz), clazz.getName) + } + + val shortScenarioDescriptions = simulationParams.populationBuilders.map(pb => ShortScenarioDescription(pb.scenarioBuilder.name, pb.injectionProfile.userCount)) + + val dataWriterInitResponses = dataWriters.map(_ ? Init(configuration, simulationParams.assertions, runMessage, shortScenarioDescriptions)) + + implicit val dispatcher = system.dispatcher + + val statsEngineFuture = Future.sequence(dataWriterInitResponses) + .map(_.forall(_ == true)) + .map { + case true => Success(new DataWritersStatsEngine(system, dataWriters)) + case _ => Failure(new Exception("DataWriters didn't initialize properly")) + } + + Await.result(statsEngineFuture, 5 seconds).get + } +} + +class DataWritersStatsEngine(system: ActorSystem, dataWriters: Seq[ActorRef]) extends StatsEngine { private val active = new AtomicBoolean(true) diff --git a/gatling-core/src/main/scala/io/gatling/core/stats/writer/DataWriterType.scala b/gatling-core/src/main/scala/io/gatling/core/stats/writer/DataWriterType.scala index bf2ff306a4..586f2181e4 100644 --- a/gatling-core/src/main/scala/io/gatling/core/stats/writer/DataWriterType.scala +++ b/gatling-core/src/main/scala/io/gatling/core/stats/writer/DataWriterType.scala @@ -25,6 +25,6 @@ object DataWriterType { sealed abstract class DataWriterType(val name: String, val className: String) object ConsoleDataWriterType extends DataWriterType("console", "io.gatling.core.stats.writer.ConsoleDataWriter") -object FileDataWriterType extends DataWriterType("file", "io.gatling.core.stats.writer.FileDataWriter") +object FileDataWriterType extends DataWriterType("file", "io.gatling.core.stats.writer.LogFileDataWriter") object GraphiteDataWriterType extends DataWriterType("graphite", "io.gatling.metrics.GraphiteDataWriter") object LeakReporterDataWriterType extends DataWriterType("leak", "io.gatling.core.stats.writer.LeakReporterDataWriter") diff --git a/gatling-core/src/main/scala/io/gatling/core/stats/writer/FileDataWriter.scala b/gatling-core/src/main/scala/io/gatling/core/stats/writer/LogFileDataWriter.scala similarity index 98% rename from gatling-core/src/main/scala/io/gatling/core/stats/writer/FileDataWriter.scala rename to gatling-core/src/main/scala/io/gatling/core/stats/writer/LogFileDataWriter.scala index e63e703105..95fe52fdb6 100644 --- a/gatling-core/src/main/scala/io/gatling/core/stats/writer/FileDataWriter.scala +++ b/gatling-core/src/main/scala/io/gatling/core/stats/writer/LogFileDataWriter.scala @@ -30,7 +30,7 @@ import com.dongxiguo.fastring.Fastring.Implicits._ import jodd.util.Base64 -object FileDataWriter { +object LogFileDataWriter { val Separator = '\t' @@ -125,9 +125,9 @@ case class FileData(limit: Int, buffer: ByteBuffer, encoder: CharsetEncoder, cha * * It writes the data of the simulation if a tabulation separated values file */ -class FileDataWriter extends DataWriter[FileData] { +class LogFileDataWriter extends DataWriter[FileData] { - import FileDataWriter._ + import LogFileDataWriter._ def onInit(init: Init): FileData = { diff --git a/gatling-core/src/test/scala/io/gatling/core/action/ExitHereIfFailedSpec.scala b/gatling-core/src/test/scala/io/gatling/core/action/ExitHereIfFailedSpec.scala index d7b0c1f792..1b23911779 100644 --- a/gatling-core/src/test/scala/io/gatling/core/action/ExitHereIfFailedSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/action/ExitHereIfFailedSpec.scala @@ -19,13 +19,13 @@ import akka.actor.ActorRef import akka.testkit._ import io.gatling.AkkaSpec import io.gatling.core.session.Session -import io.gatling.core.stats.DefaultStatsEngine +import io.gatling.core.stats.DataWritersStatsEngine import io.gatling.core.stats.writer.GroupMessage class ExitHereIfFailedSpec extends AkkaSpec { def createExitHereIfFailed(exitProbe: TestProbe, datawriterProbe: TestProbe) = - TestActorRef(ExitHereIfFailed.props(exitProbe.ref, new DefaultStatsEngine(system, List(datawriterProbe.ref)), self)) + TestActorRef(ExitHereIfFailed.props(exitProbe.ref, new DataWritersStatsEngine(system, List(datawriterProbe.ref)), self)) "ExitHereIfFailed" should "send the session to the next actor if the session was not failed" in { val exitProbe = TestProbe() diff --git a/gatling-core/src/test/scala/io/gatling/core/action/FeedSpec.scala b/gatling-core/src/test/scala/io/gatling/core/action/FeedSpec.scala index 3ce8c30562..3f4087dd1b 100644 --- a/gatling-core/src/test/scala/io/gatling/core/action/FeedSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/action/FeedSpec.scala @@ -18,7 +18,7 @@ package io.gatling.core.action import io.gatling.AkkaSpec import io.gatling.commons.validation._ import io.gatling.core.session._ -import io.gatling.core.stats.DefaultStatsEngine +import io.gatling.core.stats.DataWritersStatsEngine import akka.testkit._ @@ -26,7 +26,7 @@ class FeedSpec extends AkkaSpec { "Feed" should "send a FeedMessage to the SingletonFeed actor" in { val dataWriterProbe = TestProbe() - val statsEngine = new DefaultStatsEngine(system, List(dataWriterProbe.ref)) + val statsEngine = new DataWritersStatsEngine(system, List(dataWriterProbe.ref)) val singleton = TestProbe() val controller = TestProbe() val number: Expression[Int] = session => 1.success diff --git a/gatling-core/src/test/scala/io/gatling/core/action/GroupEndSpec.scala b/gatling-core/src/test/scala/io/gatling/core/action/GroupEndSpec.scala index 3999ab8cd1..2f62186f18 100644 --- a/gatling-core/src/test/scala/io/gatling/core/action/GroupEndSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/action/GroupEndSpec.scala @@ -18,14 +18,14 @@ package io.gatling.core.action import akka.testkit._ import io.gatling.AkkaSpec import io.gatling.core.session.Session -import io.gatling.core.stats.DefaultStatsEngine +import io.gatling.core.stats.DataWritersStatsEngine import io.gatling.core.stats.writer.GroupMessage class GroupEndSpec extends AkkaSpec { "GroupEnd" should "exit the current group" in { val dataWriterProbe = TestProbe() - val statsEngine = new DefaultStatsEngine(system, List(dataWriterProbe.ref)) + val statsEngine = new DataWritersStatsEngine(system, List(dataWriterProbe.ref)) val groupEnd = TestActorRef(GroupEnd.props(statsEngine, self)) diff --git a/gatling-core/src/test/scala/io/gatling/core/action/GroupStartSpec.scala b/gatling-core/src/test/scala/io/gatling/core/action/GroupStartSpec.scala index 0cb5cd8bd5..3f4cf05725 100644 --- a/gatling-core/src/test/scala/io/gatling/core/action/GroupStartSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/action/GroupStartSpec.scala @@ -15,19 +15,18 @@ */ package io.gatling.core.action -import io.gatling.core.stats.DefaultStatsEngine - -import akka.testkit._ import io.gatling.AkkaSpec - import io.gatling.core.session.el.El import io.gatling.core.session.{ GroupBlock, Session } +import io.gatling.core.stats.DataWritersStatsEngine + +import akka.testkit._ class GroupStartSpec extends AkkaSpec { "GroupStart" should "resolve the group name from the session and create a new group" in { val dataWriterProbe = TestProbe() - val statsEngine = new DefaultStatsEngine(system, List(dataWriterProbe.ref)) + val statsEngine = new DataWritersStatsEngine(system, List(dataWriterProbe.ref)) val groupExpr = "${theGroupName}".el[String] val groupStart = TestActorRef(GroupStart.props(groupExpr, statsEngine, self)) diff --git a/gatling-core/src/test/scala/io/gatling/core/action/IfSpec.scala b/gatling-core/src/test/scala/io/gatling/core/action/IfSpec.scala index 6f9416d130..597ab774e6 100644 --- a/gatling-core/src/test/scala/io/gatling/core/action/IfSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/action/IfSpec.scala @@ -20,7 +20,7 @@ import akka.testkit._ import io.gatling.AkkaSpec import io.gatling.core.session.Session import io.gatling.core.session.el.El -import io.gatling.core.stats.DefaultStatsEngine +import io.gatling.core.stats.DataWritersStatsEngine class IfSpec extends AkkaSpec { @@ -31,7 +31,7 @@ class IfSpec extends AkkaSpec { val thenActorProbe = TestProbe() val elseActorProbe = TestProbe() val dataWriterProbe = TestProbe() - val statsEngine = new DefaultStatsEngine(system, List(dataWriterProbe.ref)) + val statsEngine = new DataWritersStatsEngine(system, List(dataWriterProbe.ref)) val ifAction = TestActorRef(If.props(condition, thenActorProbe.ref, elseActorProbe.ref, statsEngine, self)) diff --git a/gatling-core/src/test/scala/io/gatling/core/stats/writer/FileDataWriterSpec.scala b/gatling-core/src/test/scala/io/gatling/core/stats/writer/LogFileDataWriterSpec.scala similarity index 96% rename from gatling-core/src/test/scala/io/gatling/core/stats/writer/FileDataWriterSpec.scala rename to gatling-core/src/test/scala/io/gatling/core/stats/writer/LogFileDataWriterSpec.scala index 52082ec804..045aed7ba2 100644 --- a/gatling-core/src/test/scala/io/gatling/core/stats/writer/FileDataWriterSpec.scala +++ b/gatling-core/src/test/scala/io/gatling/core/stats/writer/LogFileDataWriterSpec.scala @@ -21,10 +21,10 @@ import io.gatling.commons.util.StringHelper._ import io.gatling.core.config.GatlingConfiguration import io.gatling.core.stats.message.ResponseTimings -class FileDataWriterSpec extends BaseSpec { +class LogFileDataWriterSpec extends BaseSpec { implicit val configuration = GatlingConfiguration.loadForTest() - import FileDataWriter._ + import LogFileDataWriter._ def logMessage(record: ResponseMessage)(implicit serializer: DataWriterMessageSerializer[ResponseMessage]): String = serializer.serialize(record).toString diff --git a/gatling-http/src/main/scala/io/gatling/http/ahc/AhcFactory.scala b/gatling-http/src/main/scala/io/gatling/http/ahc/AhcFactory.scala index 446dc48d04..5746608868 100644 --- a/gatling-http/src/main/scala/io/gatling/http/ahc/AhcFactory.scala +++ b/gatling-http/src/main/scala/io/gatling/http/ahc/AhcFactory.scala @@ -18,7 +18,6 @@ package io.gatling.http.ahc import java.util.{ ArrayList => JArrayList } import java.util.concurrent.{ ExecutorService, TimeUnit, ThreadFactory, Executors } -import io.gatling.commons.util.ReflectionHelper._ import io.gatling.core.{ CoreComponents, ConfigKeys } import io.gatling.core.config.GatlingConfiguration import io.gatling.core.session.Session @@ -40,11 +39,15 @@ import org.asynchttpclient.ws.WebSocketListener private[gatling] object AhcFactory { - val AhcFactorySystemProperty = "gatling.ahcFactory" - - def apply(system: ActorSystem, coreComponents: CoreComponents)(implicit configuration: GatlingConfiguration): AhcFactory = - sys.props.get(AhcFactorySystemProperty).map(newInstance[AhcFactory](_, system, coreComponents, configuration)) - .getOrElse(new DefaultAhcFactory(system, coreComponents)) + def apply(system: ActorSystem, coreComponents: CoreComponents)(implicit configuration: GatlingConfiguration): AhcFactory = { + // + // + // + // + // + // + new DefaultAhcFactory(system, coreComponents) + } } private[gatling] trait AhcFactory { diff --git a/gatling-http/src/test/scala/io/gatling/http/action/async/polling/PollerActorSpec.scala b/gatling-http/src/test/scala/io/gatling/http/action/async/polling/PollerActorSpec.scala index 90f2c353a8..b4635c0f21 100644 --- a/gatling-http/src/test/scala/io/gatling/http/action/async/polling/PollerActorSpec.scala +++ b/gatling-http/src/test/scala/io/gatling/http/action/async/polling/PollerActorSpec.scala @@ -22,7 +22,7 @@ import io.gatling.AkkaSpec import io.gatling.commons.validation.Failure import io.gatling.core.session._ import io.gatling.core.config.GatlingConfiguration -import io.gatling.core.stats.DefaultStatsEngine +import io.gatling.core.stats.DataWritersStatsEngine import io.gatling.core.stats.writer.ErrorMessage import io.gatling.http.ahc.HttpEngine import io.gatling.http.cache.HttpCaches @@ -112,7 +112,7 @@ class PollerActorSpec extends AkkaSpec { requestDef = requestDef, responseBuilderFactory = mock[ResponseBuilderFactory], httpComponents = HttpComponents(HttpProtocol(configuration), httpEngine, mock[HttpCaches]), - statsEngine = new DefaultStatsEngine(system, List(dataWriterProbe.ref)) + statsEngine = new DataWritersStatsEngine(system, List(dataWriterProbe.ref)) ) )