diff options
author | Diego <diegolparra@gmail.com> | 2014-10-18 23:57:26 -0300 |
---|---|---|
committer | Diego <diegolparra@gmail.com> | 2014-10-18 23:57:26 -0300 |
commit | fc22c55f1c5caac4a4921855c30b966722ec8157 (patch) | |
tree | b3791628007315c645e05c2974940c2b08c5b3bd | |
parent | 0f4847445b31a2a76897f7405512a58fb4d4a1dd (diff) | |
download | Kamon-fc22c55f1c5caac4a4921855c30b966722ec8157.tar.gz Kamon-fc22c55f1c5caac4a4921855c30b966722ec8157.tar.bz2 Kamon-fc22c55f1c5caac4a4921855c30b966722ec8157.zip |
! kamon-newrelic: * Avoid reporting data to Newrelic if no metrics have been collected
* Implement error handling with NewRelic Agent * Minor refactor * close issue #7 and issue #17
10 files changed, 256 insertions, 129 deletions
diff --git a/kamon-core/src/main/scala/kamon/AkkaExtensionSwap.scala b/kamon-core/src/main/scala/kamon/AkkaExtensionSwap.scala index c0994f2c..b7050c59 100644 --- a/kamon-core/src/main/scala/kamon/AkkaExtensionSwap.scala +++ b/kamon-core/src/main/scala/kamon/AkkaExtensionSwap.scala @@ -1,18 +1,19 @@ -/* =================================================== - * Copyright © 2013 the kamon project <http://kamon.io/> +/* + * ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> * - * 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 + * 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 + * 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. - * ========================================================== */ + * 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 kamon import akka.actor.{ Extension, ActorSystem, ExtensionId } diff --git a/kamon-newrelic/src/main/resources/reference.conf b/kamon-newrelic/src/main/resources/reference.conf index 13aaca2f..059420f9 100644 --- a/kamon-newrelic/src/main/resources/reference.conf +++ b/kamon-newrelic/src/main/resources/reference.conf @@ -8,6 +8,13 @@ kamon { app-name = "Kamon[Development]" license-key = e7d350b14228f3d28f35bc3140df2c3e565ea5d5 + + # delay between connection attempts to NewRelic collector + retry-delay = 30 seconds + + # attempts to send pending metrics in the next tick, + # combining the current metrics plus the pending, after max-retry, deletes all pending metrics + max-retry = 3 } } diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala index 9c4075eb..25fbc9db 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/Agent.scala @@ -1,37 +1,46 @@ -/* =================================================== - * Copyright © 2013 the kamon project <http://kamon.io/> +/* + * ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> * - * 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 + * 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 + * 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. - * ========================================================== */ + * 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 kamon.newrelic +import java.util.concurrent.TimeUnit.{ MILLISECONDS ⇒ milliseconds } + import akka.actor.{ ActorLogging, Actor } +import akka.event.LoggingAdapter +import org.slf4j.LoggerFactory import spray.json._ -import scala.concurrent.Future +import scala.concurrent.{ ExecutionContext, Future } import spray.httpx.{ SprayJsonSupport, RequestBuilding, ResponseTransformation } import spray.httpx.encoding.Deflate import spray.http._ import spray.json.lenses.JsonLenses._ -import akka.pattern.pipe import java.lang.management.ManagementFactory import spray.client.pipelining._ -import scala.util.control.NonFatal +import scala.util.{ Failure, Success } import spray.http.Uri.Query import kamon.newrelic.MetricTranslator.TimeSliceMetrics +import scala.concurrent.duration._ class Agent extends Actor with RequestBuilding with ResponseTransformation with SprayJsonSupport with ActorLogging { + import context.dispatcher import Agent._ + import Retry._ + + self ! Initialize val agentInfo = { val config = context.system.settings.config.getConfig("kamon.newrelic") @@ -40,8 +49,10 @@ class Agent extends Actor with RequestBuilding with ResponseTransformation with // Name has the format of pid@host val runtimeName = ManagementFactory.getRuntimeMXBean.getName.split('@') + val retryDelay = FiniteDuration(config.getDuration("retry-delay", milliseconds), milliseconds) + val maxRetry = config.getInt("max-retry") - AgentInfo(licenseKey, appName, runtimeName(1), runtimeName(0).toInt) + AgentInfo(licenseKey, appName, runtimeName(1), runtimeName(0).toInt, maxRetry, retryDelay) } val baseQuery = Query( @@ -49,33 +60,36 @@ class Agent extends Actor with RequestBuilding with ResponseTransformation with "marshal_format" -> "json", "protocol_version" -> "12") - def receive = { - case Initialize(runId, collector) ⇒ - log.info("Agent initialized with runID: [{}] and collector: [{}]", runId, collector) - context become reporting(runId, collector) + def receive: Receive = uninitialized + + def uninitialized: Receive = { + case Initialize ⇒ { + connectToCollector onComplete { + case Success(agent) ⇒ { + log.info("Agent initialized with runID: [{}] and collector: [{}]", agent.runId, agent.collector) + context become reporting(agent.runId, agent.collector) + } + case Failure(reason) ⇒ self ! InitializationFailed(reason) + } + } + case InitializationFailed(reason) ⇒ { + log.info("Initialization failed: {}, retrying in {} seconds", reason.getMessage, agentInfo.retryDelay.toSeconds) + context.system.scheduler.scheduleOnce(agentInfo.retryDelay, self, Initialize) + } + case everythingElse ⇒ //ignore } def reporting(runId: Long, collector: String): Receive = { case metrics: TimeSliceMetrics ⇒ sendMetricData(runId, collector, metrics) } - override def preStart(): Unit = { - super.preStart() - initialize - } - - def initialize: Unit = { - pipe({ - for ( - collector ← selectCollector; - runId ← connect(collector, agentInfo) - ) yield Initialize(runId, collector) - } recover { - case NonFatal(ex) ⇒ InitializationFailed(ex) - }) to self - } + def connectToCollector: Future[Initialized] = for { + collector ← selectCollector + runId ← connect(collector, agentInfo) + } yield Initialized(runId, collector) import AgentJsonProtocol._ + val compressedPipeline: HttpRequest ⇒ Future[HttpResponse] = encode(Deflate) ~> sendReceive val compressedToJsonPipeline: HttpRequest ⇒ Future[JsValue] = compressedPipeline ~> toJson @@ -111,19 +125,49 @@ class Agent extends Actor with RequestBuilding with ResponseTransformation with val query = ("method" -> "metric_data") +: ("run_id" -> runId.toString) +: baseQuery val sendMetricDataUri = Uri(s"http://$collector/agent_listener/invoke_raw_method").withQuery(query) - compressedPipeline { - Post(sendMetricDataUri, MetricData(runId, metrics)) + withMaxAttempts(agentInfo.maxRetry, metrics, log) { currentMetrics ⇒ + compressedPipeline { + log.info("Sending metrics to NewRelic collector") + Post(sendMetricDataUri, MetricData(runId, currentMetrics)) + } } } - } object Agent { - - case class Initialize(runId: Long, collector: String) + case class Initialize() + case class Initialized(runId: Long, collector: String) case class InitializationFailed(reason: Throwable) case class CollectorSelection(return_value: String) - case class AgentInfo(licenseKey: String, appName: String, host: String, pid: Int) - + case class AgentInfo(licenseKey: String, appName: String, host: String, pid: Int, maxRetry: Int = 0, retryDelay: FiniteDuration) case class MetricData(runId: Long, timeSliceMetrics: TimeSliceMetrics) +} + +object Retry { + + @volatile private var attempts: Int = 0 + @volatile private var pendingMetrics: Option[TimeSliceMetrics] = None + + def withMaxAttempts[T](maxRetry: Int, metrics: TimeSliceMetrics, log: LoggingAdapter)(block: TimeSliceMetrics ⇒ Future[T])(implicit executor: ExecutionContext): Unit = { + + val currentMetrics = metrics.merge(pendingMetrics) + + if (currentMetrics.metrics.nonEmpty) { + block(currentMetrics) onComplete { + case Success(_) ⇒ + pendingMetrics = None + attempts = 0 + case Failure(_) ⇒ + attempts += 1 + if (maxRetry > attempts) { + log.info("Trying to send metrics to NewRelic collector, attempt [{}] of [{}]", attempts, maxRetry) + pendingMetrics = Some(currentMetrics) + } else { + log.info("Max attempts achieved, proceeding to remove all pending metrics") + pendingMetrics = None + attempts = 0 + } + } + } + } }
\ No newline at end of file diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/MetricTranslator.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/MetricTranslator.scala index a3bb6311..6313a2aa 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/MetricTranslator.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/MetricTranslator.scala @@ -35,7 +35,11 @@ class MetricTranslator(receiver: ActorRef) extends Actor } object MetricTranslator { - case class TimeSliceMetrics(from: Long, to: Long, metrics: Seq[NewRelic.Metric]) + case class TimeSliceMetrics(from: Long, to: Long, metrics: Seq[NewRelic.Metric]) { + def merge(thatMetrics: Option[TimeSliceMetrics]): TimeSliceMetrics = { + thatMetrics.map(that ⇒ TimeSliceMetrics(from + that.from, to + that.to, metrics ++ that.metrics)).getOrElse(this) + } + } def props(receiver: ActorRef): Props = Props(new MetricTranslator(receiver)) } diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelic.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelic.scala index fd97b2c0..b270d228 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelic.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelic.scala @@ -1,28 +1,31 @@ -/* =================================================== - * Copyright © 2013 the kamon project <http://kamon.io/> +/* + * ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> * - * 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 + * 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 + * 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. - * ========================================================== */ + * 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 kamon.newrelic +import java.util.concurrent.TimeUnit.MILLISECONDS + +import akka.actor import akka.actor._ -import kamon.metric.UserMetrics.{ UserGauges, UserMinMaxCounters, UserCounters, UserHistograms } -import scala.concurrent.duration._ import kamon.Kamon -import kamon.metric.{ UserMetrics, TickMetricSnapshotBuffer, TraceMetrics, Metrics } import kamon.metric.Subscriptions.TickMetricSnapshot -import akka.actor -import java.util.concurrent.TimeUnit.MILLISECONDS +import kamon.metric.UserMetrics.{ UserCounters, UserGauges, UserHistograms, UserMinMaxCounters } +import kamon.metric.{ Metrics, TickMetricSnapshotBuffer, TraceMetrics } + +import scala.concurrent.duration._ class NewRelicExtension(system: ExtendedActorSystem) extends Kamon.Extension { val config = system.settings.config.getConfig("kamon.newrelic") diff --git a/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala b/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala index 4203f81f..4bb0ad3a 100644 --- a/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala +++ b/kamon-newrelic/src/main/scala/kamon/newrelic/NewRelicErrorLogger.scala @@ -1,23 +1,23 @@ -/* =================================================== - * Copyright © 2013 the kamon project <http://kamon.io/> +/* + * ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> * - * 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 + * 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 + * 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. - * ========================================================== */ + * 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 kamon.newrelic -import akka.actor.{ ActorLogging, Actor } -import akka.event.Logging.Error -import akka.event.Logging.{ LoggerInitialized, InitializeLogger } +import akka.actor.{ Actor, ActorLogging } +import akka.event.Logging.{ Error, InitializeLogger, LoggerInitialized } import com.newrelic.api.agent.{ NewRelic ⇒ NR } import kamon.trace.TraceContextAware @@ -33,16 +33,10 @@ class NewRelicErrorLogger extends Actor with ActorLogging { def notifyError(error: Error): Unit = { val params = new java.util.HashMap[String, String]() - if (error.isInstanceOf[TraceContextAware]) { - val ctx = error.asInstanceOf[TraceContextAware].traceContext + val ctx = error.asInstanceOf[TraceContextAware].traceContext - for (c ← ctx) { - params.put("TraceToken", c.token) - } - } else if (!aspectJMissingAlreadyReported) { - log.warning("ASPECTJ WEAVER MISSING. You might have missed to include the javaagent JVM startup parameter in" + - " your application. Please refer to http://kamon.io/get-started/ for instructions on how to do it.") - aspectJMissingAlreadyReported = true + for (c ← ctx) { + params.put("TraceToken", c.token) } if (error.cause == Error.NoCause) { diff --git a/kamon-newrelic/src/test/scala/kamon/newrelic/AgentSpec.scala b/kamon-newrelic/src/test/scala/kamon/newrelic/AgentSpec.scala index 28dcde79..a3785d17 100644 --- a/kamon-newrelic/src/test/scala/kamon/newrelic/AgentSpec.scala +++ b/kamon-newrelic/src/test/scala/kamon/newrelic/AgentSpec.scala @@ -1,57 +1,111 @@ -/* =================================================== - * Copyright © 2013 the kamon project <http://kamon.io/> +/* + * ========================================================================================= + * Copyright © 2013-2014 the kamon project <http://kamon.io/> * - * 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 + * 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 + * 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. - * ========================================================== */ + * 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 kamon.newrelic -import akka.testkit.{ TestActor, TestProbe, TestKit } -import akka.actor.{ Props, ActorRef, ActorSystem } -import org.scalatest.WordSpecLike +import akka.actor.{ ActorRef, ActorSystem, Props } +import akka.io.IO +import akka.testkit.TestActor.{ AutoPilot, KeepRunning } +import akka.testkit._ +import com.typesafe.config.ConfigFactory import kamon.AkkaExtensionSwap +import kamon.newrelic.MetricTranslator.TimeSliceMetrics +import org.scalatest.{ BeforeAndAfterAll, WordSpecLike } import spray.can.Http -import akka.io.IO -import akka.testkit.TestActor.{ KeepRunning, AutoPilot } -import spray.http._ -import spray.http.HttpRequest -import spray.http.HttpResponse +import spray.http.{ HttpRequest, HttpResponse, _ } + +class AgentSpec extends TestKitBase with WordSpecLike with BeforeAndAfterAll { -class AgentSpec extends TestKit(ActorSystem("agent-spec")) with WordSpecLike { + import kamon.newrelic.AgentSpec._ + + implicit lazy val system: ActorSystem = ActorSystem("Agent-Spec", ConfigFactory.parseString( + """ + |akka { + | loggers = ["akka.testkit.TestEventListener"] + | loglevel = "INFO" + |} + |kamon { + | newrelic { + | retry-delay = 1 second + | max-retry = 3 + | } + |} + | + """.stripMargin)) + + var agent: ActorRef = _ setupFakeHttpManager "the Newrelic Agent" should { - "try to connect upon creation" in { - val agent = system.actorOf(Props[Agent]) + "try to connect upon creation, retry to connect if an error occurs" in { + EventFilter.info(message = "Initialization failed: Unexpected response from HTTP transport: None, retrying in 1 seconds", occurrences = 3).intercept { + system.actorOf(Props[Agent]) + Thread.sleep(1000) + } + } + + "when everything is fine should select a NewRelic collector" in { + EventFilter.info(message = "Agent initialized with runID: [161221111] and collector: [collector-8.newrelic.com]", occurrences = 1).intercept { + system.actorOf(Props[Agent]) + } + } + + "merge the metrics if not possible send them and do it in the next post" in { + EventFilter.info(pattern = "Trying to send metrics to NewRelic collector, attempt.*", occurrences = 2).intercept { + agent = system.actorOf(Props[Agent].withDispatcher(CallingThreadDispatcher.Id)) + + for (_ ← 1 to 3) { + sendDelayedMetric(agent) + } + } + } - Thread.sleep(5000) + "when the connection is re-established, the metrics should be send" in { + EventFilter.info(message = "Sending metrics to NewRelic collector", occurrences = 2).intercept { + sendDelayedMetric(agent) + } } } def setupFakeHttpManager: Unit = { + val ConnectionAttempts = 3 // an arbitrary value only for testing purposes + val PostAttempts = 3 // if the number is achieved, the metrics should be discarded val fakeHttpManager = TestProbe() + var attemptsToConnect: Int = 0 // should retry grab an NewRelic collector after retry-delay + var attemptsToSendMetrics: Int = 0 + fakeHttpManager.setAutoPilot(new TestActor.AutoPilot { def run(sender: ActorRef, msg: Any): AutoPilot = { msg match { case HttpRequest(_, uri, _, _, _) if rawMethodIs("get_redirect_host", uri) ⇒ - sender ! jsonResponse( - """ + if (attemptsToConnect == ConnectionAttempts) { + sender ! jsonResponse( + """ | { | "return_value": "collector-8.newrelic.com" | } | """.stripMargin) + system.log.info("Selecting Collector") - println("Selecting Collector") + } else { + sender ! None + attemptsToConnect += 1 + system.log.info("Network Error or Connection Refuse") + } case HttpRequest(_, uri, _, _, _) if rawMethodIs("connect", uri) ⇒ sender ! jsonResponse( @@ -62,9 +116,17 @@ class AgentSpec extends TestKit(ActorSystem("agent-spec")) with WordSpecLike { | } | } | """.stripMargin) - println("Connecting") - } + system.log.info("Connecting") + case HttpRequest(_, uri, _, _, _) if rawMethodIs("metric_data", uri) ⇒ + if (attemptsToSendMetrics < PostAttempts) { + sender ! None + attemptsToSendMetrics += 1 + system.log.info("Error when trying to send metrics to NewRelic collector, the metrics will be merged") + } else { + system.log.info("Sending metrics to NewRelic collector") + } + } KeepRunning } @@ -81,4 +143,16 @@ class AgentSpec extends TestKit(ActorSystem("agent-spec")) with WordSpecLike { def manager: ActorRef = fakeHttpManager.ref }) } + + override def afterAll() { + super.afterAll() + system.shutdown() + } } + +object AgentSpec { + def sendDelayedMetric(agent: ActorRef, delay: Int = 1000): Unit = { + agent ! TimeSliceMetrics(100000L, 200000L, Seq(NewRelic.Metric("Latency", None, 1000L, 2000D, 3000D, 1D, 100000D, 300D))) + Thread.sleep(delay) + } +}
\ No newline at end of file diff --git a/kamon-playground/src/main/resources/application.conf b/kamon-playground/src/main/resources/application.conf index 58c07644..b4a6636a 100644 --- a/kamon-playground/src/main/resources/application.conf +++ b/kamon-playground/src/main/resources/application.conf @@ -1,6 +1,6 @@ akka { loglevel = INFO - extensions = ["kamon.statsd.StatsD"] + extensions = ["kamon.newrelic.NewRelic"] actor { debug { diff --git a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala index b6bcc677..3e6e982e 100644 --- a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala +++ b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala @@ -129,7 +129,7 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil path("segment") { complete { val segment = TraceRecorder.startSegment(HttpClientRequest("hello-world")) - (replier ? "hello").mapTo[String].onComplete { t => + (replier ? "hello").mapTo[String].onComplete { t ⇒ segment.get.finish() } diff --git a/project/Projects.scala b/project/Projects.scala index 140508ad..8828a62d 100644 --- a/project/Projects.scala +++ b/project/Projects.scala @@ -49,8 +49,8 @@ object Projects extends Build { .settings(aspectJSettings: _*) .settings( libraryDependencies ++= - compile(aspectJ, sprayCan, sprayClient, sprayRouting, sprayJson, sprayJsonLenses, newrelic, snakeYaml) ++ - test(scalatest, akkaTestKit, sprayTestkit, slf4Api, slf4nop)) + compile(aspectJ, sprayCan, sprayClient, sprayRouting, sprayJson, sprayJsonLenses, newrelic, snakeYaml, akkaSlf4j) ++ + test(scalatest, akkaTestKit, sprayTestkit, slf4Api, akkaSlf4j)) .dependsOn(kamonCore) |