diff options
author | Ivan Topolnjak <ivantopo@gmail.com> | 2015-03-12 01:09:44 +0100 |
---|---|---|
committer | Ivan Topolnjak <ivantopo@gmail.com> | 2015-03-12 01:09:44 +0100 |
commit | 33f063eeb11d3381a2a66e5421ab046cdd0b57e5 (patch) | |
tree | f2bb7af8c1b26c57033b17f7ff988d5a73a9aa91 | |
parent | ce47f95b1e94a2f8ec51a5c58061e54005cccbde (diff) | |
download | Kamon-33f063eeb11d3381a2a66e5421ab046cdd0b57e5.tar.gz Kamon-33f063eeb11d3381a2a66e5421ab046cdd0b57e5.tar.bz2 Kamon-33f063eeb11d3381a2a66e5421ab046cdd0b57e5.zip |
= core: put the ModuleLoader back in place for auto-starting modules.
11 files changed, 143 insertions, 252 deletions
diff --git a/kamon-core/src/main/resources/META-INF/aop.xml b/kamon-core/src/main/resources/META-INF/aop.xml index 2ffb8b09..b13f9aac 100644 --- a/kamon-core/src/main/resources/META-INF/aop.xml +++ b/kamon-core/src/main/resources/META-INF/aop.xml @@ -4,7 +4,7 @@ <aspects> <!-- Notify that AspectJ is present --> - <aspect name="kamon.supervisor.AspectJPresent"/> + <aspect name="kamon.AspectJPresent"/> </aspects> diff --git a/kamon-core/src/main/scala/kamon/Kamon.scala b/kamon-core/src/main/scala/kamon/Kamon.scala index a6469039..f8253875 100644 --- a/kamon-core/src/main/scala/kamon/Kamon.scala +++ b/kamon-core/src/main/scala/kamon/Kamon.scala @@ -48,6 +48,7 @@ object Kamon { metrics.start(_system) tracer.start(_system) + _system.registerExtension(ModuleLoader) } else sys.error("Kamon has already been started.") } diff --git a/kamon-core/src/main/scala/kamon/ModuleLoader.scala b/kamon-core/src/main/scala/kamon/ModuleLoader.scala new file mode 100644 index 00000000..84d93943 --- /dev/null +++ b/kamon-core/src/main/scala/kamon/ModuleLoader.scala @@ -0,0 +1,109 @@ +package kamon + +import _root_.akka.actor +import _root_.akka.actor._ +import _root_.akka.event.Logging +import org.aspectj.lang.ProceedingJoinPoint +import org.aspectj.lang.annotation.{Around, Pointcut, Aspect} + + +private[kamon] object ModuleLoader extends ExtensionId[ModuleLoaderExtension] with ExtensionIdProvider { + def lookup(): ExtensionId[_ <: actor.Extension] = ModuleLoader + def createExtension(system: ExtendedActorSystem): ModuleLoaderExtension = new ModuleLoaderExtension(system) +} + +private[kamon] class ModuleLoaderExtension(system: ExtendedActorSystem) extends Kamon.Extension { + val log = Logging(system, "ModuleLoader") + val settings = ModuleLoaderSettings(system) + + + if (settings.modulesRequiringAspectJ.nonEmpty && !isAspectJPresent && settings.showAspectJMissingWarning) + logAspectJWeaverMissing(settings.modulesRequiringAspectJ) + + // Force initialization of all modules marked with auto-start. + settings.availableModules.filter(_.autoStart).foreach { module ⇒ + if (module.extensionClass == "none") + log.debug("Ignoring auto start of the [{}] module with no extension class.") + else + system.dynamicAccess.getObjectFor[ExtensionId[Kamon.Extension]](module.extensionClass).map { moduleID ⇒ + log.debug("Auto starting the [{}] module.", module.name) + moduleID.get(system) + + } recover { + case th: Throwable ⇒ log.error(th, "Failed to auto start the [{}] module.", module.name) + } + + } + + // When AspectJ is present the kamon.supervisor.AspectJPresent aspect will make this return true. + def isAspectJPresent: Boolean = false + + def logAspectJWeaverMissing(modulesRequiringAspectJ: List[AvailableModuleInfo]): Unit = { + val moduleNames = modulesRequiringAspectJ.map(_.name).mkString(", ") + val weaverMissingMessage = + """ + | + | ___ _ ___ _ _ ___ ___ _ _ + | / _ \ | | |_ | | | | | | \/ |(_) (_) + |/ /_\ \ ___ _ __ ___ ___ | |_ | | | | | | ___ __ _ __ __ ___ _ __ | . . | _ ___ ___ _ _ __ __ _ + || _ |/ __|| '_ \ / _ \ / __|| __| | | | |/\| | / _ \ / _` |\ \ / // _ \| '__| | |\/| || |/ __|/ __|| || '_ \ / _` | + || | | |\__ \| |_) || __/| (__ | |_ /\__/ / \ /\ /| __/| (_| | \ V /| __/| | | | | || |\__ \\__ \| || | | || (_| | + |\_| |_/|___/| .__/ \___| \___| \__|\____/ \/ \/ \___| \__,_| \_/ \___||_| \_| |_/|_||___/|___/|_||_| |_| \__, | + | | | __/ | + | |_| |___/ + | + | It seems like your application was not started with the -javaagent:/path-to-aspectj-weaver.jar option but Kamon detected + | the following modules which require AspecJ to work properly: + | + """.stripMargin + moduleNames + + """ + | + | If you need help on setting up the aspectj weaver go to http://kamon.io/introduction/get-started/ for more info. On the + | other hand, if you are sure that you do not need or do not want to use the weaver then you can disable this error message + | by changing the kamon.show-aspectj-missing-warning setting in your configuration file. + | + """.stripMargin + + log.error(weaverMissingMessage) + } +} + +private[kamon] case class AvailableModuleInfo(name: String, extensionClass: String, requiresAspectJ: Boolean, autoStart: Boolean) +private[kamon] case class ModuleLoaderSettings(showAspectJMissingWarning: Boolean, availableModules: List[AvailableModuleInfo]) { + val modulesRequiringAspectJ = availableModules.filter(_.requiresAspectJ) +} + +private[kamon] object ModuleLoaderSettings { + + def apply(system: ActorSystem): ModuleLoaderSettings = { + import kamon.util.ConfigTools.Syntax + + val config = system.settings.config.getConfig("kamon.modules") + val showAspectJMissingWarning = system.settings.config.getBoolean("kamon.show-aspectj-missing-warning") + + val modules = config.firstLevelKeys + val availableModules = modules.map { moduleName ⇒ + val moduleConfig = config.getConfig(moduleName) + + AvailableModuleInfo( + moduleName, + moduleConfig.getString("extension-id"), + moduleConfig.getBoolean("requires-aspectj"), + moduleConfig.getBoolean("auto-start")) + + } toList + + ModuleLoaderSettings(showAspectJMissingWarning, availableModules) + } +} + +@Aspect +private[kamon] class AspectJPresent { + + @Pointcut("execution(* kamon.ModuleLoaderExtension.isAspectJPresent())") + def isAspectJPresentAtModuleSupervisor(): Unit = {} + + @Around("isAspectJPresentAtModuleSupervisor()") + def aroundIsAspectJPresentAtModuleSupervisor(pjp: ProceedingJoinPoint): Boolean = true + +} diff --git a/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala b/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala deleted file mode 100644 index 4dc5ff41..00000000 --- a/kamon-core/src/main/scala/kamon/instrumentation/AspectJWeaverMissingWarning.scala +++ /dev/null @@ -1,33 +0,0 @@ -/* - * ========================================================================================= - * Copyright © 2013-2015 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 - * - * 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 kamon.instrumentation - -import _root_.akka.event.EventStream -import org.aspectj.lang.ProceedingJoinPoint -import org.aspectj.lang.annotation.{ Around, Pointcut, Aspect } - -@Aspect -class AspectJWeaverMissingWarning { - - @Pointcut("execution(* kamon.metric.MetricsExtension.printInitializationMessage(..)) && args(eventStream, *)") - def printInitializationMessage(eventStream: EventStream): Unit = {} - - @Around("printInitializationMessage(eventStream)") - def aroundPrintInitializationMessage(pjp: ProceedingJoinPoint, eventStream: EventStream): Unit = { - pjp.proceed(Array[AnyRef](eventStream, Boolean.box(true))) - } -} diff --git a/kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala b/kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala deleted file mode 100644 index 0df9539f..00000000 --- a/kamon-core/src/main/scala/kamon/supervisor/AspectJPresent.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* - * ========================================================================================= - * Copyright © 2013-2015 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 - * - * 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 kamon.supervisor - -import org.aspectj.lang.ProceedingJoinPoint -import org.aspectj.lang.annotation.{ Around, Aspect, Pointcut } - -@Aspect -class AspectJPresent { - - @Pointcut("execution(* kamon.supervisor.KamonSupervisor.isAspectJPresent())") - def isAspectJPresentAtModuleSupervisor(): Unit = {} - - @Around("isAspectJPresentAtModuleSupervisor()") - def aroundIsAspectJPresentAtModuleSupervisor(pjp: ProceedingJoinPoint): Boolean = true - -} diff --git a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala b/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala deleted file mode 100644 index ddce63fb..00000000 --- a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorExtension.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* - * ========================================================================================= - * Copyright © 2013-2015 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 - * - * 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 kamon.supervisor - -import akka.actor -import akka.actor._ -import kamon.Kamon -import kamon.supervisor.KamonSupervisor.CreateModule - -import scala.concurrent.{ Promise, Future } -import scala.util.Success - -object ModuleSupervisor extends ExtensionId[ModuleSupervisorExtension] with ExtensionIdProvider { - def lookup(): ExtensionId[_ <: actor.Extension] = ModuleSupervisor - def createExtension(system: ExtendedActorSystem): ModuleSupervisorExtension = new ModuleSupervisorExtensionImpl(system) -} - -trait ModuleSupervisorExtension extends actor.Extension { - def createModule(name: String, props: Props): Future[ActorRef] -} - -class ModuleSupervisorExtensionImpl(system: ExtendedActorSystem) extends ModuleSupervisorExtension { - import system.dispatcher - - private val _settings = ModuleSupervisorSettings(system) - private val _supervisor = system.actorOf(KamonSupervisor.props(_settings, system.dynamicAccess), "kamon") - - def createModule(name: String, props: Props): Future[ActorRef] = Future {} flatMap { _: Unit ⇒ - val modulePromise = Promise[ActorRef]() - _supervisor ! CreateModule(name, props, modulePromise) - modulePromise.future - } -} - -class KamonSupervisor(settings: ModuleSupervisorSettings, dynamicAccess: DynamicAccess) extends Actor with ActorLogging { - - init() - - def receive = { - case CreateModule(name, props, childPromise) ⇒ createChildModule(name, props, childPromise) - } - - def createChildModule(name: String, props: Props, childPromise: Promise[ActorRef]): Unit = - context.child(name).map { alreadyAvailableModule ⇒ - log.warning("Received a request to create module [{}] but the module is already available, returning the existent instance.") - childPromise.complete(Success(alreadyAvailableModule)) - - } getOrElse (childPromise.complete(Success(context.actorOf(props, name)))) - - def init(): Unit = { - if (settings.modulesRequiringAspectJ.nonEmpty && !isAspectJPresent && settings.showAspectJMissingWarning) - logAspectJWeaverMissing(settings.modulesRequiringAspectJ) - - // Force initialization of all modules marked with auto-start. - settings.availableModules.filter(_.autoStart).foreach { module ⇒ - if (module.extensionClass == "none") - log.debug("Ignoring auto start of the [{}] module with no extension class.") - else - dynamicAccess.getObjectFor[ExtensionId[Kamon.Extension]](module.extensionClass).map { moduleID ⇒ - moduleID.get(context.system) - log.debug("Auto starting the [{}] module.", module.name) - - } recover { - case th: Throwable ⇒ log.error(th, "Failed to auto start the [{}] module.", module.name) - } - - } - } - - // When AspectJ is present the kamon.supervisor.AspectJPresent aspect will make this return true. - def isAspectJPresent: Boolean = false - - def logAspectJWeaverMissing(modulesRequiringAspectJ: List[AvailableModuleInfo]): Unit = { - val moduleNames = modulesRequiringAspectJ.map(_.name).mkString(", ") - val weaverMissingMessage = - """ - | - | ___ _ ___ _ _ ___ ___ _ _ - | / _ \ | | |_ | | | | | | \/ |(_) (_) - |/ /_\ \ ___ _ __ ___ ___ | |_ | | | | | | ___ __ _ __ __ ___ _ __ | . . | _ ___ ___ _ _ __ __ _ - || _ |/ __|| '_ \ / _ \ / __|| __| | | | |/\| | / _ \ / _` |\ \ / // _ \| '__| | |\/| || |/ __|/ __|| || '_ \ / _` | - || | | |\__ \| |_) || __/| (__ | |_ /\__/ / \ /\ /| __/| (_| | \ V /| __/| | | | | || |\__ \\__ \| || | | || (_| | - |\_| |_/|___/| .__/ \___| \___| \__|\____/ \/ \/ \___| \__,_| \_/ \___||_| \_| |_/|_||___/|___/|_||_| |_| \__, | - | | | __/ | - | |_| |___/ - | - | It seems like your application was not started with the -javaagent:/path-to-aspectj-weaver.jar option but Kamon detected - | the following modules which require AspecJ to work properly: - | - """.stripMargin + moduleNames + - """ - | - | If you need help on setting up the aspectj weaver go to http://kamon.io/introduction/get-started/ for more info. On the - | other hand, if you are sure that you do not need or do not want to use the weaver then you can disable this error message - | by changing the kamon.show-aspectj-missing-warning setting in your configuration file. - | - """.stripMargin - - log.error(weaverMissingMessage) - } - -} - -object KamonSupervisor { - case class CreateModule(name: String, props: Props, childPromise: Promise[ActorRef]) - - def props(settings: ModuleSupervisorSettings, dynamicAccess: DynamicAccess): Props = - Props(new KamonSupervisor(settings, dynamicAccess)) - -} - diff --git a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala b/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala deleted file mode 100644 index c04157aa..00000000 --- a/kamon-core/src/main/scala/kamon/supervisor/ModuleSupervisorSettings.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* - * ========================================================================================= - * Copyright © 2013-2015 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 - * - * 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 kamon.supervisor - -import akka.actor.ActorSystem - -case class AvailableModuleInfo(name: String, extensionClass: String, requiresAspectJ: Boolean, autoStart: Boolean) -case class ModuleSupervisorSettings(showAspectJMissingWarning: Boolean, availableModules: List[AvailableModuleInfo]) { - val modulesRequiringAspectJ = availableModules.filter(_.requiresAspectJ) -} - -object ModuleSupervisorSettings { - - def apply(system: ActorSystem): ModuleSupervisorSettings = { - import kamon.util.ConfigTools.Syntax - - val config = system.settings.config.getConfig("kamon.modules") - val showAspectJMissingWarning = system.settings.config.getBoolean("kamon.show-aspectj-missing-warning") - - val modules = config.firstLevelKeys - val availableModules = modules.map { moduleName ⇒ - val moduleConfig = config.getConfig(moduleName) - - AvailableModuleInfo( - moduleName, - moduleConfig.getString("extension-id"), - moduleConfig.getBoolean("requires-aspectj"), - moduleConfig.getBoolean("auto-start")) - - } toList - - ModuleSupervisorSettings(showAspectJMissingWarning, availableModules) - } - -} diff --git a/kamon-core/src/main/scala/kamon/util/MapMerge.scala b/kamon-core/src/main/scala/kamon/util/MapMerge.scala index 64b4f7ae..8573358b 100644 --- a/kamon-core/src/main/scala/kamon/util/MapMerge.scala +++ b/kamon-core/src/main/scala/kamon/util/MapMerge.scala @@ -19,7 +19,7 @@ package kamon.util object MapMerge { /** - * Merge to immutable maps with the same key and value types, using the provided valueMerge function. + * Merge two immutable maps with the same key and value types, using the provided valueMerge function. */ implicit class Syntax[K, V](val map: Map[K, V]) extends AnyVal { def merge(that: Map[K, V], valueMerge: (V, V) ⇒ V): Map[K, V] = { diff --git a/kamon-playground/src/main/resources/application.conf b/kamon-playground/src/main/resources/application.conf index 74e710bc..4922a5ba 100644 --- a/kamon-playground/src/main/resources/application.conf +++ b/kamon-playground/src/main/resources/application.conf @@ -31,10 +31,14 @@ kamon { license-key = e7d350b14228f3d28f35bc3140df2c3e565ea5d5 } + internal-config { + akka.loglevel = DEBUG + } + modules { kamon-newrelic.auto-start = no kamon-datadog.auto-start = no - kamon-log-reporter.auto-start = no + kamon-log-reporter.auto-start = yes kamon-system-metrics.auto-start = no } } diff --git a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala index e4cafb45..280158c0 100644 --- a/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala +++ b/kamon-playground/src/main/scala/test/SimpleRequestProcessor.scala @@ -37,8 +37,8 @@ object SimpleRequestProcessor extends App with SimpleRoutingApp with RequestBuil import scala.concurrent.duration._ - implicit val system = ActorSystem("test") Kamon.start() + implicit val system = ActorSystem("test") import test.SimpleRequestProcessor.system.dispatcher val printer = system.actorOf(Props[PrintWhatever]) diff --git a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala index ebdaf01f..630cc7a2 100644 --- a/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala +++ b/kamon-system-metrics/src/main/scala/kamon/system/SystemMetricsExtension.scala @@ -16,18 +16,19 @@ package kamon.system import java.io.File +import akka.actor.SupervisorStrategy.Restart import akka.actor._ import akka.event.Logging -import kamon.supervisor.ModuleSupervisor import kamon.system.custom.{ ContextSwitchesUpdater, ContextSwitchesMetrics } import kamon.system.jmx._ import kamon.Kamon -import kamon.metric._ import kamon.sigar.SigarProvisioner import kamon.system.sigar.SigarMetricsUpdater import kamon.util.ConfigTools.Syntax +import scala.concurrent.duration.FiniteDuration + object SystemMetrics extends ExtensionId[SystemMetricsExtension] with ExtensionIdProvider { override def lookup(): ExtensionId[_ <: Extension] = SystemMetrics override def createExtension(system: ExtendedActorSystem): SystemMetricsExtension = new SystemMetricsExtension(system) @@ -44,10 +45,9 @@ class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extensio val contextSwitchesRefreshInterval = config.getFiniteDuration("context-switches-refresh-interval") val metricsExtension = Kamon.metrics - // Sigar-based metrics SigarProvisioner.provision(new File(sigarFolder)) - val sigarMetricsRecorder = ModuleSupervisor.get(system).createModule("sigar-metrics-recorder", - SigarMetricsUpdater.props(sigarRefreshInterval).withDispatcher("kamon.system-metrics.sigar-dispatcher")) + + val supervisor = system.actorOf(SystemMetricsSupervisor.props(sigarRefreshInterval, contextSwitchesRefreshInterval), "kamon-system-metrics") // JMX Metrics ClassLoadingMetrics.register(metricsExtension) @@ -55,17 +55,32 @@ class SystemMetricsExtension(system: ExtendedActorSystem) extends Kamon.Extensio HeapMemoryMetrics.register(metricsExtension) NonHeapMemoryMetrics.register(metricsExtension) ThreadsMetrics.register(metricsExtension) +} + +class SystemMetricsSupervisor(sigarRefreshInterval: FiniteDuration, contextSwitchesRefreshInterval: FiniteDuration) extends Actor { + + // Sigar metrics recorder + context.actorOf(SigarMetricsUpdater.props(sigarRefreshInterval) + .withDispatcher("kamon.system-metrics.sigar-dispatcher"), "sigar-metrics-recorder") // If we are in Linux, add ContextSwitchesMetrics as well. if (isLinux) { - val contextSwitchesRecorder = ContextSwitchesMetrics.register(system, contextSwitchesRefreshInterval) - - ModuleSupervisor.get(system).createModule("context-switches-metrics-recorder", - ContextSwitchesUpdater.props(contextSwitchesRecorder, sigarRefreshInterval) - .withDispatcher("kamon.system-metrics.context-switches-dispatcher")) + val contextSwitchesRecorder = ContextSwitchesMetrics.register(context.system, contextSwitchesRefreshInterval) + context.actorOf(ContextSwitchesUpdater.props(contextSwitchesRecorder, sigarRefreshInterval) + .withDispatcher("kamon.system-metrics.context-switches-dispatcher"), "context-switches-metrics-recorder") } def isLinux: Boolean = System.getProperty("os.name").indexOf("Linux") != -1 + def receive = Actor.emptyBehavior + + override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy() { + case anyException => Restart + } +} + +object SystemMetricsSupervisor { + def props(sigarRefreshInterval: FiniteDuration, contextSwitchesRefreshInterval: FiniteDuration): Props = + Props(new SystemMetricsSupervisor(sigarRefreshInterval, contextSwitchesRefreshInterval)) }
\ No newline at end of file |