mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:11:45 +03:00
Update profiling CLI arguments (#3461)
This commit is contained in:
parent
9a188344a8
commit
f9d2964e83
@ -235,6 +235,7 @@
|
|||||||
[3444]: https://github.com/enso-org/enso/pull/3444
|
[3444]: https://github.com/enso-org/enso/pull/3444
|
||||||
[3453]: https://github.com/enso-org/enso/pull/3453
|
[3453]: https://github.com/enso-org/enso/pull/3453
|
||||||
[3454]: https://github.com/enso-org/enso/pull/3454
|
[3454]: https://github.com/enso-org/enso/pull/3454
|
||||||
|
[3461]: https://github.com/enso-org/enso/pull/3461
|
||||||
[3465]: https://github.com/enso-org/enso/pull/3465
|
[3465]: https://github.com/enso-org/enso/pull/3465
|
||||||
|
|
||||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||||
|
@ -905,9 +905,10 @@ lazy val `json-rpc-server` = project
|
|||||||
libraryDependencies ++= akka ++ akkaTest,
|
libraryDependencies ++= akka ++ akkaTest,
|
||||||
libraryDependencies ++= circe,
|
libraryDependencies ++= circe,
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"io.circe" %% "circe-literal" % circeVersion,
|
"io.circe" %% "circe-literal" % circeVersion,
|
||||||
akkaTestkit % Test,
|
"com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion,
|
||||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
akkaTestkit % Test,
|
||||||
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,9 +15,10 @@ import org.enso.languageserver.runtime.RuntimeKiller.{
|
|||||||
ShutDownRuntime
|
ShutDownRuntime
|
||||||
}
|
}
|
||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
|
import org.enso.profiling.{FileSampler, MethodsSampler, NoopSampler}
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{Await, Future}
|
import scala.concurrent.{Await, ExecutionContextExecutor, Future}
|
||||||
|
|
||||||
/** A lifecycle component used to start and stop a Language Server.
|
/** A lifecycle component used to start and stop a Language Server.
|
||||||
*
|
*
|
||||||
@ -31,11 +32,14 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel)
|
|||||||
@volatile
|
@volatile
|
||||||
private var maybeServerCtx: Option[ServerContext] = None
|
private var maybeServerCtx: Option[ServerContext] = None
|
||||||
|
|
||||||
implicit private val ec = config.computeExecutionContext
|
implicit private val ec: ExecutionContextExecutor =
|
||||||
|
config.computeExecutionContext
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def start(): Future[ComponentStarted.type] = {
|
override def start(): Future[ComponentStarted.type] = {
|
||||||
logger.info("Starting Language Server...")
|
logger.info("Starting Language Server...")
|
||||||
|
val sampler = startSampling(config)
|
||||||
|
logger.debug(s"Started ${sampler.getClass.getName}.")
|
||||||
val module = new MainModule(config, logLevel)
|
val module = new MainModule(config, logLevel)
|
||||||
val bindJsonServer =
|
val bindJsonServer =
|
||||||
for {
|
for {
|
||||||
@ -51,7 +55,8 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel)
|
|||||||
jsonBinding <- bindJsonServer
|
jsonBinding <- bindJsonServer
|
||||||
binaryBinding <- bindBinaryServer
|
binaryBinding <- bindBinaryServer
|
||||||
_ <- Future {
|
_ <- Future {
|
||||||
maybeServerCtx = Some(ServerContext(module, jsonBinding, binaryBinding))
|
maybeServerCtx =
|
||||||
|
Some(ServerContext(sampler, module, jsonBinding, binaryBinding))
|
||||||
}
|
}
|
||||||
_ <- Future {
|
_ <- Future {
|
||||||
logger.info(
|
logger.info(
|
||||||
@ -62,14 +67,29 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel)
|
|||||||
} yield ComponentStarted
|
} yield ComponentStarted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Start the application sampling. */
|
||||||
|
private def startSampling(config: LanguageServerConfig): MethodsSampler = {
|
||||||
|
val sampler = config.profilingConfig.profilingPath match {
|
||||||
|
case Some(path) =>
|
||||||
|
new FileSampler(path.toFile)
|
||||||
|
case None =>
|
||||||
|
NoopSampler()
|
||||||
|
}
|
||||||
|
sampler.start()
|
||||||
|
config.profilingConfig.profilingTime.foreach(sampler.stop(_))
|
||||||
|
|
||||||
|
sampler
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def stop(): Future[ComponentStopped.type] =
|
override def stop(): Future[ComponentStopped.type] =
|
||||||
maybeServerCtx match {
|
maybeServerCtx match {
|
||||||
case None =>
|
case None =>
|
||||||
Future.failed(new Exception("Server isn't running"))
|
Future.successful(ComponentStopped)
|
||||||
|
|
||||||
case Some(serverContext) =>
|
case Some(serverContext) =>
|
||||||
for {
|
for {
|
||||||
|
_ <- stopSampling(serverContext)
|
||||||
_ <- terminateTruffle(serverContext)
|
_ <- terminateTruffle(serverContext)
|
||||||
_ <- terminateAkka(serverContext)
|
_ <- terminateAkka(serverContext)
|
||||||
_ <- releaseResources(serverContext)
|
_ <- releaseResources(serverContext)
|
||||||
@ -77,6 +97,9 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel)
|
|||||||
} yield ComponentStopped
|
} yield ComponentStopped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def stopSampling(serverContext: ServerContext): Future[Unit] =
|
||||||
|
Future(serverContext.sampler.stop()).recover(logError)
|
||||||
|
|
||||||
private def releaseResources(serverContext: ServerContext): Future[Unit] =
|
private def releaseResources(serverContext: ServerContext): Future[Unit] =
|
||||||
for {
|
for {
|
||||||
_ <- Future(serverContext.mainModule.close()).recover(logError)
|
_ <- Future(serverContext.mainModule.close()).recover(logError)
|
||||||
@ -101,7 +124,7 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def terminateTruffle(serverContext: ServerContext): Future[Unit] = {
|
private def terminateTruffle(serverContext: ServerContext): Future[Unit] = {
|
||||||
implicit val askTimeout = Timeout(12.seconds)
|
implicit val askTimeout: Timeout = Timeout(12.seconds)
|
||||||
val killFiber =
|
val killFiber =
|
||||||
(serverContext.mainModule.runtimeKiller ? ShutDownRuntime)
|
(serverContext.mainModule.runtimeKiller ? ShutDownRuntime)
|
||||||
.mapTo[RuntimeShutdownResult]
|
.mapTo[RuntimeShutdownResult]
|
||||||
@ -129,11 +152,13 @@ object LanguageServerComponent {
|
|||||||
|
|
||||||
/** A running server context.
|
/** A running server context.
|
||||||
*
|
*
|
||||||
|
* @param sampler a sampler gathering the application performance statistics
|
||||||
* @param mainModule a main module containing all components of the server
|
* @param mainModule a main module containing all components of the server
|
||||||
* @param jsonBinding a http binding for rpc protocol
|
* @param jsonBinding a http binding for rpc protocol
|
||||||
* @param binaryBinding a http binding for data protocol
|
* @param binaryBinding a http binding for data protocol
|
||||||
*/
|
*/
|
||||||
case class ServerContext(
|
case class ServerContext(
|
||||||
|
sampler: MethodsSampler,
|
||||||
mainModule: MainModule,
|
mainModule: MainModule,
|
||||||
jsonBinding: Http.ServerBinding,
|
jsonBinding: Http.ServerBinding,
|
||||||
binaryBinding: Http.ServerBinding
|
binaryBinding: Http.ServerBinding
|
||||||
|
@ -2,7 +2,7 @@ package org.enso.languageserver.boot
|
|||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor}
|
||||||
|
|
||||||
/** The config of the running Language Server instance.
|
/** The config of the running Language Server instance.
|
||||||
*
|
*
|
||||||
@ -11,7 +11,7 @@ import scala.concurrent.ExecutionContext
|
|||||||
* @param dataPort a data port that the server listen to
|
* @param dataPort a data port that the server listen to
|
||||||
* @param contentRootUuid an id of content root
|
* @param contentRootUuid an id of content root
|
||||||
* @param contentRootPath a path to the content root
|
* @param contentRootPath a path to the content root
|
||||||
* @param isProfilingEnabled is the application profiling enabled
|
* @param profilingConfig an application profiling configuration
|
||||||
*/
|
*/
|
||||||
case class LanguageServerConfig(
|
case class LanguageServerConfig(
|
||||||
interface: String,
|
interface: String,
|
||||||
@ -19,7 +19,7 @@ case class LanguageServerConfig(
|
|||||||
dataPort: Int,
|
dataPort: Int,
|
||||||
contentRootUuid: UUID,
|
contentRootUuid: UUID,
|
||||||
contentRootPath: String,
|
contentRootPath: String,
|
||||||
isProfilingEnabled: Boolean,
|
profilingConfig: ProfilingConfig,
|
||||||
name: String = "language-server",
|
name: String = "language-server",
|
||||||
computeExecutionContext: ExecutionContext = ExecutionContext.global
|
computeExecutionContext: ExecutionContextExecutor = ExecutionContext.global
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,6 @@ import org.enso.lockmanager.server.LockManagerService
|
|||||||
import org.enso.logger.masking.Masking
|
import org.enso.logger.masking.Masking
|
||||||
import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel}
|
import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel}
|
||||||
import org.enso.polyglot.{RuntimeOptions, RuntimeServerInfo}
|
import org.enso.polyglot.{RuntimeOptions, RuntimeServerInfo}
|
||||||
import org.enso.profiling.{NoopSampler, TempFileSampler}
|
|
||||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
|
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
|
||||||
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
|
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
|
||||||
import org.graalvm.polyglot.Context
|
import org.graalvm.polyglot.Context
|
||||||
@ -56,6 +55,7 @@ import java.net.URI
|
|||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
/** A main module containing all components of the server.
|
/** A main module containing all components of the server.
|
||||||
*
|
*
|
||||||
@ -83,7 +83,8 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(),
|
ExecutionContextConfig(),
|
||||||
directoriesConfig
|
directoriesConfig,
|
||||||
|
serverConfig.profilingConfig
|
||||||
)
|
)
|
||||||
log.trace("Created Language Server config [{}].", languageServerConfig)
|
log.trace("Created Language Server config [{}].", languageServerConfig)
|
||||||
|
|
||||||
@ -148,8 +149,20 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val runtimeEventsMonitor =
|
val runtimeEventsMonitor =
|
||||||
if (logLevel == LogLevel.Trace) ApiEventsMonitor()
|
languageServerConfig.profiling.runtimeEventsLogPath match {
|
||||||
else new NoopEventsMonitor
|
case Some(path) =>
|
||||||
|
ApiEventsMonitor(path) match {
|
||||||
|
case Success(monitor) =>
|
||||||
|
monitor
|
||||||
|
case Failure(exception) =>
|
||||||
|
log.error(
|
||||||
|
s"Failed to create runtime events monitor for $path ($exception)."
|
||||||
|
)
|
||||||
|
new NoopEventsMonitor
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
new NoopEventsMonitor
|
||||||
|
}
|
||||||
log.trace(
|
log.trace(
|
||||||
s"Started runtime events monitor ${runtimeEventsMonitor.getClass.getName}."
|
s"Started runtime events monitor ${runtimeEventsMonitor.getClass.getName}."
|
||||||
)
|
)
|
||||||
@ -228,12 +241,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
|
|||||||
languageServerConfig,
|
languageServerConfig,
|
||||||
RuntimeFailureMapper(contentRootManagerWrapper),
|
RuntimeFailureMapper(contentRootManagerWrapper),
|
||||||
runtimeConnector,
|
runtimeConnector,
|
||||||
sessionRouter,
|
sessionRouter
|
||||||
if (serverConfig.isProfilingEnabled) {
|
|
||||||
val s = TempFileSampler("context-registry")
|
|
||||||
JavaLoggingLogHandler.registerLogFile(s.getSiblingFile(".log"))
|
|
||||||
s
|
|
||||||
} else NoopSampler()
|
|
||||||
),
|
),
|
||||||
"context-registry"
|
"context-registry"
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.enso.languageserver.boot
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
/** Application profiling configuration.
|
||||||
|
*
|
||||||
|
* @param runtimeEventsLogPath the path to the runtime events log file
|
||||||
|
* @param profilingPath the path to the profiling output file
|
||||||
|
* @param profilingTime limit the profiling duration, as an infinite profiling
|
||||||
|
* duration may cause out-of-memory errors.
|
||||||
|
*/
|
||||||
|
case class ProfilingConfig(
|
||||||
|
runtimeEventsLogPath: Option[Path] = None,
|
||||||
|
profilingPath: Option[Path] = None,
|
||||||
|
profilingTime: Option[FiniteDuration] = None
|
||||||
|
)
|
@ -1,10 +1,12 @@
|
|||||||
package org.enso.languageserver.data
|
package org.enso.languageserver.data
|
||||||
|
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.filemanager.ContentRootWithFile
|
import org.enso.languageserver.filemanager.ContentRootWithFile
|
||||||
import org.enso.logger.masking.{MaskingUtils, ToLogString}
|
import org.enso.logger.masking.{MaskingUtils, ToLogString}
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
/** Configuration of the path watcher.
|
/** Configuration of the path watcher.
|
||||||
@ -122,13 +124,15 @@ object ProjectDirectoriesConfig {
|
|||||||
* @param pathWatcher the path watcher config
|
* @param pathWatcher the path watcher config
|
||||||
* @param executionContext the executionContext config
|
* @param executionContext the executionContext config
|
||||||
* @param directories the configuration of internal directories
|
* @param directories the configuration of internal directories
|
||||||
|
* @param profiling the profiling configuration
|
||||||
*/
|
*/
|
||||||
case class Config(
|
case class Config(
|
||||||
projectContentRoot: ContentRootWithFile,
|
projectContentRoot: ContentRootWithFile,
|
||||||
fileManager: FileManagerConfig,
|
fileManager: FileManagerConfig,
|
||||||
pathWatcher: PathWatcherConfig,
|
pathWatcher: PathWatcherConfig,
|
||||||
executionContext: ExecutionContextConfig,
|
executionContext: ExecutionContextConfig,
|
||||||
directories: ProjectDirectoriesConfig
|
directories: ProjectDirectoriesConfig,
|
||||||
|
profiling: ProfilingConfig
|
||||||
) extends ToLogString {
|
) extends ToLogString {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
|
@ -8,6 +8,8 @@ import java.nio.charset.StandardCharsets
|
|||||||
import java.nio.file.{Files, Path, StandardOpenOption}
|
import java.nio.file.{Files, Path, StandardOpenOption}
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
|
|
||||||
|
import scala.util.Try
|
||||||
|
|
||||||
/** Gather messages between the language server and the runtime and write them
|
/** Gather messages between the language server and the runtime and write them
|
||||||
* to the provided file in CSV format.
|
* to the provided file in CSV format.
|
||||||
*
|
*
|
||||||
@ -61,12 +63,16 @@ final class ApiEventsMonitor(path: Path, clock: Clock) extends EventsMonitor {
|
|||||||
}
|
}
|
||||||
object ApiEventsMonitor {
|
object ApiEventsMonitor {
|
||||||
|
|
||||||
/** Create default instance of [[ApiEventsMonitor]]. */
|
/** Create default instance of [[ApiEventsMonitor]].
|
||||||
def apply(): ApiEventsMonitor =
|
*
|
||||||
new ApiEventsMonitor(
|
* @param path the path to the events log file
|
||||||
Files.createTempFile("enso-api-events-", ".csv"),
|
* @return an instance of [[ApiEventsMonitor]]
|
||||||
Clock.systemUTC()
|
*/
|
||||||
)
|
def apply(path: Path): Try[ApiEventsMonitor] = Try {
|
||||||
|
Files.deleteIfExists(path)
|
||||||
|
Files.createFile(path)
|
||||||
|
new ApiEventsMonitor(path, Clock.systemUTC())
|
||||||
|
}
|
||||||
|
|
||||||
/** Direction of the message. */
|
/** Direction of the message. */
|
||||||
sealed trait Direction
|
sealed trait Direction
|
||||||
|
@ -13,7 +13,6 @@ import org.enso.languageserver.util.UnhandledLogging
|
|||||||
import org.enso.logger.akka.ActorMessageLogging
|
import org.enso.logger.akka.ActorMessageLogging
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
import org.enso.polyglot.runtime.Runtime.Api.ContextId
|
import org.enso.polyglot.runtime.Runtime.Api.ContextId
|
||||||
import org.enso.profiling.MethodsSampler
|
|
||||||
import org.enso.searcher.SuggestionsRepo
|
import org.enso.searcher.SuggestionsRepo
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@ -59,15 +58,13 @@ import scala.concurrent.duration._
|
|||||||
* @param runtimeFailureMapper mapper for runtime failures
|
* @param runtimeFailureMapper mapper for runtime failures
|
||||||
* @param runtime reference to the [[RuntimeConnector]]
|
* @param runtime reference to the [[RuntimeConnector]]
|
||||||
* @param sessionRouter the session router
|
* @param sessionRouter the session router
|
||||||
* @param sampler the methods sampler
|
|
||||||
*/
|
*/
|
||||||
final class ContextRegistry(
|
final class ContextRegistry(
|
||||||
repo: SuggestionsRepo[Future],
|
repo: SuggestionsRepo[Future],
|
||||||
config: Config,
|
config: Config,
|
||||||
runtimeFailureMapper: RuntimeFailureMapper,
|
runtimeFailureMapper: RuntimeFailureMapper,
|
||||||
runtime: ActorRef,
|
runtime: ActorRef,
|
||||||
sessionRouter: ActorRef,
|
sessionRouter: ActorRef
|
||||||
sampler: MethodsSampler
|
|
||||||
) extends Actor
|
) extends Actor
|
||||||
with LazyLogging
|
with LazyLogging
|
||||||
with ActorMessageLogging
|
with ActorMessageLogging
|
||||||
@ -75,7 +72,8 @@ final class ContextRegistry(
|
|||||||
|
|
||||||
import ContextRegistryProtocol._
|
import ContextRegistryProtocol._
|
||||||
|
|
||||||
private val timeout: FiniteDuration = config.executionContext.requestTimeout
|
private val timeout: FiniteDuration =
|
||||||
|
config.executionContext.requestTimeout
|
||||||
|
|
||||||
override def preStart(): Unit = {
|
override def preStart(): Unit = {
|
||||||
context.system.eventStream
|
context.system.eventStream
|
||||||
@ -110,11 +108,9 @@ final class ContextRegistry(
|
|||||||
.foreach(_ ! update)
|
.foreach(_ ! update)
|
||||||
|
|
||||||
case update: Api.ExecutionFailed =>
|
case update: Api.ExecutionFailed =>
|
||||||
sampler.stop(6.seconds)(context.dispatcher)
|
|
||||||
store.getListener(update.contextId).foreach(_ ! update)
|
store.getListener(update.contextId).foreach(_ ! update)
|
||||||
|
|
||||||
case update: Api.ExecutionComplete =>
|
case update: Api.ExecutionComplete =>
|
||||||
sampler.stop(6.seconds)(context.dispatcher)
|
|
||||||
store.getListener(update.contextId).foreach(_ ! update)
|
store.getListener(update.contextId).foreach(_ ! update)
|
||||||
|
|
||||||
case update: Api.ExecutionUpdate =>
|
case update: Api.ExecutionUpdate =>
|
||||||
@ -166,7 +162,6 @@ final class ContextRegistry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PushContextRequest(client, contextId, stackItem) =>
|
case PushContextRequest(client, contextId, stackItem) =>
|
||||||
sampler.start()
|
|
||||||
if (store.hasContext(client.clientId, contextId)) {
|
if (store.hasContext(client.clientId, contextId)) {
|
||||||
val item = getRuntimeStackItem(stackItem)
|
val item = getRuntimeStackItem(stackItem)
|
||||||
val handler =
|
val handler =
|
||||||
@ -400,15 +395,13 @@ object ContextRegistry {
|
|||||||
* @param runtimeFailureMapper mapper for runtime failures
|
* @param runtimeFailureMapper mapper for runtime failures
|
||||||
* @param runtime reference to the [[RuntimeConnector]]
|
* @param runtime reference to the [[RuntimeConnector]]
|
||||||
* @param sessionRouter the session router
|
* @param sessionRouter the session router
|
||||||
* @param sampler the methods sampler
|
|
||||||
*/
|
*/
|
||||||
def props(
|
def props(
|
||||||
repo: SuggestionsRepo[Future],
|
repo: SuggestionsRepo[Future],
|
||||||
config: Config,
|
config: Config,
|
||||||
runtimeFailureMapper: RuntimeFailureMapper,
|
runtimeFailureMapper: RuntimeFailureMapper,
|
||||||
runtime: ActorRef,
|
runtime: ActorRef,
|
||||||
sessionRouter: ActorRef,
|
sessionRouter: ActorRef
|
||||||
sampler: MethodsSampler
|
|
||||||
): Props =
|
): Props =
|
||||||
Props(
|
Props(
|
||||||
new ContextRegistry(
|
new ContextRegistry(
|
||||||
@ -416,8 +409,7 @@ object ContextRegistry {
|
|||||||
config,
|
config,
|
||||||
runtimeFailureMapper,
|
runtimeFailureMapper,
|
||||||
runtime,
|
runtime,
|
||||||
sessionRouter,
|
sessionRouter
|
||||||
sampler
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package org.enso.languageserver.boot.resource
|
|||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.data._
|
import org.enso.languageserver.data._
|
||||||
import org.enso.languageserver.event.InitializedEvent
|
import org.enso.languageserver.event.InitializedEvent
|
||||||
import org.enso.languageserver.filemanager.{ContentRoot, ContentRootWithFile}
|
import org.enso.languageserver.filemanager.{ContentRoot, ContentRootWithFile}
|
||||||
@ -20,6 +21,7 @@ import org.sqlite.SQLiteException
|
|||||||
|
|
||||||
import java.nio.file.{Files, StandardOpenOption}
|
import java.nio.file.{Files, StandardOpenOption}
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
@ -210,7 +212,8 @@ class RepoInitializationSpec
|
|||||||
FileManagerConfig(timeout = 3.seconds.dilated),
|
FileManagerConfig(timeout = 3.seconds.dilated),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds.dilated),
|
ExecutionContextConfig(requestTimeout = 3.seconds.dilated),
|
||||||
ProjectDirectoriesConfig.initialize(root.file)
|
ProjectDirectoriesConfig.initialize(root.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.enso.languageserver.filemanager
|
|||||||
import akka.actor.{ActorRef, ActorSystem}
|
import akka.actor.{ActorRef, ActorSystem}
|
||||||
import akka.testkit.{TestDuration, TestKit, TestProbe}
|
import akka.testkit.{TestDuration, TestKit, TestProbe}
|
||||||
import org.apache.commons.lang3.SystemUtils
|
import org.apache.commons.lang3.SystemUtils
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.data._
|
import org.enso.languageserver.data._
|
||||||
import org.enso.languageserver.filemanager.ContentRootManagerProtocol.{
|
import org.enso.languageserver.filemanager.ContentRootManagerProtocol.{
|
||||||
ContentRootsAddedNotification,
|
ContentRootsAddedNotification,
|
||||||
@ -19,6 +20,7 @@ import org.scalatest.{Inside, OptionValues}
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.{Path => JPath}
|
import java.nio.file.{Path => JPath}
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
|
|
||||||
class ContentRootManagerSpec
|
class ContentRootManagerSpec
|
||||||
@ -46,7 +48,8 @@ class ContentRootManagerSpec
|
|||||||
FileManagerConfig(timeout = 3.seconds.dilated),
|
FileManagerConfig(timeout = 3.seconds.dilated),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds.dilated),
|
ExecutionContextConfig(requestTimeout = 3.seconds.dilated),
|
||||||
ProjectDirectoriesConfig.initialize(root.file)
|
ProjectDirectoriesConfig.initialize(root.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
rootActor = system.actorOf(ContentRootManagerActor.props(config))
|
rootActor = system.actorOf(ContentRootManagerActor.props(config))
|
||||||
rootManager = new ContentRootManagerWrapper(config, rootActor)
|
rootManager = new ContentRootManagerWrapper(config, rootActor)
|
||||||
|
@ -3,6 +3,7 @@ package org.enso.languageserver.runtime
|
|||||||
import akka.actor.{ActorRef, ActorSystem}
|
import akka.actor.{ActorRef, ActorSystem}
|
||||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.data._
|
import org.enso.languageserver.data._
|
||||||
import org.enso.languageserver.event.InitializedEvent
|
import org.enso.languageserver.event.InitializedEvent
|
||||||
import org.enso.languageserver.filemanager.{
|
import org.enso.languageserver.filemanager.{
|
||||||
@ -29,6 +30,7 @@ import org.scalatest.wordspec.AnyWordSpecLike
|
|||||||
|
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{Await, Future}
|
import scala.concurrent.{Await, Future}
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
@ -453,7 +455,8 @@ class ContextEventsListenerSpec
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds),
|
ExecutionContextConfig(requestTimeout = 3.seconds),
|
||||||
ProjectDirectoriesConfig.initialize(root.file)
|
ProjectDirectoriesConfig.initialize(root.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import akka.actor.{ActorRef, ActorSystem}
|
|||||||
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
import akka.testkit.{ImplicitSender, TestKit, TestProbe}
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.enso.docs.generator.DocsGenerator
|
import org.enso.docs.generator.DocsGenerator
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.capability.CapabilityProtocol.{
|
import org.enso.languageserver.capability.CapabilityProtocol.{
|
||||||
AcquireCapability,
|
AcquireCapability,
|
||||||
CapabilityAcquired
|
CapabilityAcquired
|
||||||
@ -1107,7 +1108,8 @@ class SuggestionsHandlerSpec
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds),
|
ExecutionContextConfig(requestTimeout = 3.seconds),
|
||||||
ProjectDirectoriesConfig.initialize(root.file)
|
ProjectDirectoriesConfig.initialize(root.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import akka.actor.{ActorRef, Props}
|
|||||||
import akka.http.scaladsl.model.RemoteAddress
|
import akka.http.scaladsl.model.RemoteAddress
|
||||||
import com.google.flatbuffers.FlatBufferBuilder
|
import com.google.flatbuffers.FlatBufferBuilder
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.data.{
|
import org.enso.languageserver.data.{
|
||||||
Config,
|
Config,
|
||||||
ExecutionContextConfig,
|
ExecutionContextConfig,
|
||||||
@ -45,7 +46,8 @@ class BaseBinaryServerTest extends BinaryServerTestKit {
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds),
|
ExecutionContextConfig(requestTimeout = 3.seconds),
|
||||||
ProjectDirectoriesConfig.initialize(testContentRoot.file)
|
ProjectDirectoriesConfig.initialize(testContentRoot.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.addShutdownHook(FileUtils.deleteQuietly(testContentRoot.file))
|
sys.addShutdownHook(FileUtils.deleteQuietly(testContentRoot.file))
|
||||||
|
@ -12,6 +12,7 @@ import org.enso.editions.{EditionResolver, Editions}
|
|||||||
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
import org.enso.jsonrpc.test.JsonRpcServerTestKit
|
||||||
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
import org.enso.jsonrpc.{ClientControllerFactory, Protocol}
|
||||||
import org.enso.languageserver.TestClock
|
import org.enso.languageserver.TestClock
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
import org.enso.languageserver.boot.resource.{
|
import org.enso.languageserver.boot.resource.{
|
||||||
DirectoriesInitialization,
|
DirectoriesInitialization,
|
||||||
RepoInitialization,
|
RepoInitialization,
|
||||||
@ -41,7 +42,6 @@ import org.enso.loggingservice.LogLevel
|
|||||||
import org.enso.pkg.PackageManager
|
import org.enso.pkg.PackageManager
|
||||||
import org.enso.polyglot.data.TypeGraph
|
import org.enso.polyglot.data.TypeGraph
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
import org.enso.profiling.NoopSampler
|
|
||||||
import org.enso.runtimeversionmanager.test.{
|
import org.enso.runtimeversionmanager.test.{
|
||||||
FakeEnvironment,
|
FakeEnvironment,
|
||||||
TestableThreadSafeFileLockManager
|
TestableThreadSafeFileLockManager
|
||||||
@ -93,7 +93,8 @@ class BaseServerTest
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds),
|
ExecutionContextConfig(requestTimeout = 3.seconds),
|
||||||
ProjectDirectoriesConfig(testContentRoot.file)
|
ProjectDirectoriesConfig(testContentRoot.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
|
|
||||||
override def protocol: Protocol = JsonRpc.protocol
|
override def protocol: Protocol = JsonRpc.protocol
|
||||||
@ -189,8 +190,7 @@ class BaseServerTest
|
|||||||
config,
|
config,
|
||||||
RuntimeFailureMapper(contentRootManagerWrapper),
|
RuntimeFailureMapper(contentRootManagerWrapper),
|
||||||
runtimeConnectorProbe.ref,
|
runtimeConnectorProbe.ref,
|
||||||
sessionRouter,
|
sessionRouter
|
||||||
NoopSampler()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package org.enso.languageserver.websocket.json
|
package org.enso.languageserver.websocket.json
|
||||||
|
|
||||||
|
import io.circe.literal._
|
||||||
|
import io.circe.parser.parse
|
||||||
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.bouncycastle.util.encoders.Hex
|
||||||
|
import org.enso.languageserver.boot.ProfilingConfig
|
||||||
|
import org.enso.languageserver.data._
|
||||||
|
import org.enso.polyglot.runtime.Runtime.Api
|
||||||
|
import org.enso.testkit.RetrySpec
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import java.nio.file.{Files, Paths}
|
import java.nio.file.{Files, Paths}
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import io.circe.literal._
|
|
||||||
import io.circe.parser.parse
|
|
||||||
import org.apache.commons.io.FileUtils
|
|
||||||
import org.bouncycastle.util.encoders.Hex
|
|
||||||
import org.enso.languageserver.data._
|
|
||||||
import org.enso.polyglot.runtime.Runtime.Api
|
|
||||||
import org.enso.testkit.RetrySpec
|
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class FileManagerTest extends BaseServerTest with RetrySpec {
|
class FileManagerTest extends BaseServerTest with RetrySpec {
|
||||||
@ -26,7 +27,8 @@ class FileManagerTest extends BaseServerTest with RetrySpec {
|
|||||||
FileManagerConfig(timeout = 3.seconds),
|
FileManagerConfig(timeout = 3.seconds),
|
||||||
PathWatcherConfig(),
|
PathWatcherConfig(),
|
||||||
ExecutionContextConfig(requestTimeout = 3.seconds),
|
ExecutionContextConfig(requestTimeout = 3.seconds),
|
||||||
ProjectDirectoriesConfig.initialize(testContentRoot.file)
|
ProjectDirectoriesConfig.initialize(testContentRoot.file),
|
||||||
|
ProfilingConfig()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,7 +679,8 @@ object LauncherApplication {
|
|||||||
logLevel,
|
logLevel,
|
||||||
connectLogger,
|
connectLogger,
|
||||||
globalCLIOptions.colorMode,
|
globalCLIOptions.colorMode,
|
||||||
!disableLogMasking
|
!disableLogMasking,
|
||||||
|
None
|
||||||
)
|
)
|
||||||
initializeApp()
|
initializeApp()
|
||||||
|
|
||||||
|
@ -6,7 +6,9 @@ import org.enso.languageserver.boot.{
|
|||||||
}
|
}
|
||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import java.util.concurrent.Semaphore
|
||||||
|
|
||||||
|
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.io.StdIn
|
import scala.io.StdIn
|
||||||
|
|
||||||
@ -14,6 +16,8 @@ import scala.io.StdIn
|
|||||||
*/
|
*/
|
||||||
object LanguageServerApp {
|
object LanguageServerApp {
|
||||||
|
|
||||||
|
private val semaphore = new Semaphore(1)
|
||||||
|
|
||||||
/** Runs a Language Server
|
/** Runs a Language Server
|
||||||
*
|
*
|
||||||
* @param config the application config
|
* @param config the application config
|
||||||
@ -27,7 +31,7 @@ object LanguageServerApp {
|
|||||||
): Unit = {
|
): Unit = {
|
||||||
val server = new LanguageServerComponent(config, logLevel)
|
val server = new LanguageServerComponent(config, logLevel)
|
||||||
Runtime.getRuntime.addShutdownHook(new Thread(() => {
|
Runtime.getRuntime.addShutdownHook(new Thread(() => {
|
||||||
Await.result(server.stop(), 40.seconds)
|
stop(server)(config.computeExecutionContext)
|
||||||
}))
|
}))
|
||||||
Await.result(server.start(), 1.minute)
|
Await.result(server.start(), 1.minute)
|
||||||
if (deamonize) {
|
if (deamonize) {
|
||||||
@ -37,7 +41,31 @@ object LanguageServerApp {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StdIn.readLine()
|
StdIn.readLine()
|
||||||
|
stop(server)(config.computeExecutionContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Stops the language server.
|
||||||
|
*
|
||||||
|
* @param server the language server component
|
||||||
|
* @param ec the execution context
|
||||||
|
*/
|
||||||
|
private def stop(
|
||||||
|
server: LanguageServerComponent
|
||||||
|
)(implicit ec: ExecutionContext): Unit = {
|
||||||
|
Await.ready(synchronize(server.stop()), 40.seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Makes sure that the calls to the provided future are synchronized. */
|
||||||
|
private def synchronize[A](
|
||||||
|
fut: => Future[A]
|
||||||
|
)(implicit ec: ExecutionContext): Future[A] = {
|
||||||
|
val task = for {
|
||||||
|
_ <- Future { semaphore.acquire() }
|
||||||
|
result <- fut
|
||||||
|
} yield result
|
||||||
|
task.onComplete(_ => semaphore.release())
|
||||||
|
|
||||||
|
task
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import com.typesafe.scalalogging.Logger
|
|||||||
import org.apache.commons.cli.{Option => CliOption, _}
|
import org.apache.commons.cli.{Option => CliOption, _}
|
||||||
import org.enso.editions.DefaultEdition
|
import org.enso.editions.DefaultEdition
|
||||||
import org.enso.languageserver.boot
|
import org.enso.languageserver.boot
|
||||||
import org.enso.languageserver.boot.LanguageServerConfig
|
import org.enso.languageserver.boot.{LanguageServerConfig, ProfilingConfig}
|
||||||
import org.enso.libraryupload.LibraryUploader.UploadFailedError
|
import org.enso.libraryupload.LibraryUploader.UploadFailedError
|
||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
import org.enso.pkg.{Contact, PackageManager, Template}
|
import org.enso.pkg.{Contact, PackageManager, Template}
|
||||||
@ -16,14 +16,14 @@ import org.enso.version.VersionDescription
|
|||||||
import org.graalvm.polyglot.PolyglotException
|
import org.graalvm.polyglot.PolyglotException
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.{Path, Paths}
|
||||||
import java.util.UUID
|
import java.util.{Collections, UUID}
|
||||||
|
|
||||||
import scala.Console.err
|
import scala.Console.err
|
||||||
|
import scala.concurrent.duration._
|
||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.{Failure, Success, Try}
|
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import java.util.Collections
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
/** The main CLI entry point class. */
|
/** The main CLI entry point class. */
|
||||||
object Main {
|
object Main {
|
||||||
@ -40,7 +40,10 @@ object Main {
|
|||||||
private val DOCS_OPTION = "docs"
|
private val DOCS_OPTION = "docs"
|
||||||
private val PREINSTALL_OPTION = "preinstall-dependencies"
|
private val PREINSTALL_OPTION = "preinstall-dependencies"
|
||||||
private val LANGUAGE_SERVER_OPTION = "server"
|
private val LANGUAGE_SERVER_OPTION = "server"
|
||||||
private val LANGUAGE_SERVER_PROFILING = "server-profiling"
|
private val LANGUAGE_SERVER_PROFILING_PATH = "server-profiling-path"
|
||||||
|
private val LANGUAGE_SERVER_PROFILING_TIME = "server-profiling-time"
|
||||||
|
private val LANGUAGE_SERVER_PROFILING_EVENTS_LOG_PATH =
|
||||||
|
"server-profiling-events-log-path"
|
||||||
private val DAEMONIZE_OPTION = "daemon"
|
private val DAEMONIZE_OPTION = "daemon"
|
||||||
private val INTERFACE_OPTION = "interface"
|
private val INTERFACE_OPTION = "interface"
|
||||||
private val RPC_PORT_OPTION = "rpc-port"
|
private val RPC_PORT_OPTION = "rpc-port"
|
||||||
@ -153,11 +156,26 @@ object Main {
|
|||||||
.longOpt(LANGUAGE_SERVER_OPTION)
|
.longOpt(LANGUAGE_SERVER_OPTION)
|
||||||
.desc("Runs Language Server")
|
.desc("Runs Language Server")
|
||||||
.build()
|
.build()
|
||||||
val lsProfilingOption = CliOption.builder
|
val lsProfilingPathOption = CliOption.builder
|
||||||
.longOpt(LANGUAGE_SERVER_PROFILING)
|
.hasArg(true)
|
||||||
.desc(
|
.numberOfArgs(1)
|
||||||
"Enables the Language Server profiling. The output is written to system temp directory."
|
.argName("file")
|
||||||
)
|
.longOpt(LANGUAGE_SERVER_PROFILING_PATH)
|
||||||
|
.desc("The path to the Language Server profiling file.")
|
||||||
|
.build()
|
||||||
|
val lsProfilingTimeOption = CliOption.builder
|
||||||
|
.hasArg(true)
|
||||||
|
.numberOfArgs(1)
|
||||||
|
.argName("seconds")
|
||||||
|
.longOpt(LANGUAGE_SERVER_PROFILING_TIME)
|
||||||
|
.desc("The duration in seconds limiting the profiling time.")
|
||||||
|
.build()
|
||||||
|
val lsProfilingEventsLogPathOption = CliOption.builder
|
||||||
|
.hasArg(true)
|
||||||
|
.numberOfArgs(1)
|
||||||
|
.argName("file")
|
||||||
|
.longOpt(LANGUAGE_SERVER_PROFILING_EVENTS_LOG_PATH)
|
||||||
|
.desc("The path to the runtime events log file.")
|
||||||
.build()
|
.build()
|
||||||
val deamonizeOption = CliOption.builder
|
val deamonizeOption = CliOption.builder
|
||||||
.longOpt(DAEMONIZE_OPTION)
|
.longOpt(DAEMONIZE_OPTION)
|
||||||
@ -335,7 +353,9 @@ object Main {
|
|||||||
.addOption(newProjectAuthorNameOpt)
|
.addOption(newProjectAuthorNameOpt)
|
||||||
.addOption(newProjectAuthorEmailOpt)
|
.addOption(newProjectAuthorEmailOpt)
|
||||||
.addOption(lsOption)
|
.addOption(lsOption)
|
||||||
.addOption(lsProfilingOption)
|
.addOption(lsProfilingPathOption)
|
||||||
|
.addOption(lsProfilingTimeOption)
|
||||||
|
.addOption(lsProfilingEventsLogPathOption)
|
||||||
.addOption(deamonizeOption)
|
.addOption(deamonizeOption)
|
||||||
.addOption(interfaceOption)
|
.addOption(interfaceOption)
|
||||||
.addOption(rpcPortOption)
|
.addOption(rpcPortOption)
|
||||||
@ -823,14 +843,29 @@ object Main {
|
|||||||
dataPort <- Either
|
dataPort <- Either
|
||||||
.catchNonFatal(dataPortStr.toInt)
|
.catchNonFatal(dataPortStr.toInt)
|
||||||
.leftMap(_ => "Port must be integer")
|
.leftMap(_ => "Port must be integer")
|
||||||
profilingEnabled = line.hasOption(LANGUAGE_SERVER_PROFILING)
|
profilingPathStr =
|
||||||
|
Option(line.getOptionValue(LANGUAGE_SERVER_PROFILING_PATH))
|
||||||
|
profilingPath <- Either
|
||||||
|
.catchNonFatal(profilingPathStr.map(Paths.get(_)))
|
||||||
|
.leftMap(_ => "Profiling path is invalid")
|
||||||
|
profilingTimeStr = Option(
|
||||||
|
line.getOptionValue(LANGUAGE_SERVER_PROFILING_TIME)
|
||||||
|
)
|
||||||
|
profilingTime <- Either
|
||||||
|
.catchNonFatal(profilingTimeStr.map(_.toInt.seconds))
|
||||||
|
.leftMap(_ => "Profiling time should be an integer")
|
||||||
|
profilingEventsLogPathStr =
|
||||||
|
Option(line.getOptionValue(LANGUAGE_SERVER_PROFILING_EVENTS_LOG_PATH))
|
||||||
|
profilingEventsLogPath <- Either
|
||||||
|
.catchNonFatal(profilingEventsLogPathStr.map(Paths.get(_)))
|
||||||
|
.leftMap(_ => "Profiling events log path is invalid")
|
||||||
} yield boot.LanguageServerConfig(
|
} yield boot.LanguageServerConfig(
|
||||||
interface,
|
interface,
|
||||||
rpcPort,
|
rpcPort,
|
||||||
dataPort,
|
dataPort,
|
||||||
rootId,
|
rootId,
|
||||||
rootPath,
|
rootPath,
|
||||||
profilingEnabled
|
ProfilingConfig(profilingEventsLogPath, profilingPath, profilingTime)
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Prints the version of the Enso executable.
|
/** Prints the version of the Enso executable.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.enso.jsonrpc
|
package org.enso.jsonrpc
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
import akka.NotUsed
|
import akka.NotUsed
|
||||||
import akka.actor.{ActorRef, ActorSystem, Props}
|
import akka.actor.{ActorRef, ActorSystem, Props}
|
||||||
import akka.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
@ -10,6 +9,7 @@ import akka.http.scaladsl.server.Directives._
|
|||||||
import akka.http.scaladsl.server.Route
|
import akka.http.scaladsl.server.Route
|
||||||
import akka.stream.scaladsl.{Flow, Sink, Source}
|
import akka.stream.scaladsl.{Flow, Sink, Source}
|
||||||
import akka.stream.{Materializer, OverflowStrategy}
|
import akka.stream.{Materializer, OverflowStrategy}
|
||||||
|
import com.typesafe.scalalogging.LazyLogging
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
@ -31,7 +31,7 @@ class JsonRpcServer(
|
|||||||
)(
|
)(
|
||||||
implicit val system: ActorSystem,
|
implicit val system: ActorSystem,
|
||||||
implicit val materializer: Materializer
|
implicit val materializer: Materializer
|
||||||
) {
|
) extends LazyLogging {
|
||||||
|
|
||||||
implicit val ec: ExecutionContext = system.dispatcher
|
implicit val ec: ExecutionContext = system.dispatcher
|
||||||
|
|
||||||
@ -55,6 +55,9 @@ class JsonRpcServer(
|
|||||||
_.toStrict(config.lazyMessageTimeout)
|
_.toStrict(config.lazyMessageTimeout)
|
||||||
.map(msg => MessageHandler.WebMessage(msg.text))
|
.map(msg => MessageHandler.WebMessage(msg.text))
|
||||||
)
|
)
|
||||||
|
.wireTap { webMessage =>
|
||||||
|
logger.trace(s"Received text message: ${webMessage.message}.")
|
||||||
|
}
|
||||||
.to(
|
.to(
|
||||||
Sink.actorRef[MessageHandler.WebMessage](
|
Sink.actorRef[MessageHandler.WebMessage](
|
||||||
messageHandler,
|
messageHandler,
|
||||||
@ -78,6 +81,9 @@ class JsonRpcServer(
|
|||||||
NotUsed
|
NotUsed
|
||||||
}
|
}
|
||||||
.map((outMsg: MessageHandler.WebMessage) => TextMessage(outMsg.message))
|
.map((outMsg: MessageHandler.WebMessage) => TextMessage(outMsg.message))
|
||||||
|
.wireTap { textMessage =>
|
||||||
|
logger.trace(s"Sent text message ${textMessage.text}.")
|
||||||
|
}
|
||||||
|
|
||||||
Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
|
Flow.fromSinkAndSource(incomingMessages, outgoingMessages)
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@ package org.enso.loggingservice
|
|||||||
|
|
||||||
import org.enso.loggingservice.internal.{InternalLogMessage, LoggerConnection}
|
import org.enso.loggingservice.internal.{InternalLogMessage, LoggerConnection}
|
||||||
|
|
||||||
import java.io.{File, FileWriter, Writer}
|
import java.util.logging.{Handler, Level, LogRecord}
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.util.logging.{Handler, Level, LogRecord, XMLFormatter}
|
|
||||||
|
|
||||||
/** A [[Handler]] implementation that allows to use the logging service as a
|
/** A [[Handler]] implementation that allows to use the logging service as a
|
||||||
* backend for [[java.util.logging]].
|
* backend for [[java.util.logging]].
|
||||||
@ -27,12 +25,6 @@ class JavaLoggingLogHandler(
|
|||||||
exception = Option(record.getThrown)
|
exception = Option(record.getThrown)
|
||||||
)
|
)
|
||||||
connection.send(message)
|
connection.send(message)
|
||||||
val w = JavaLoggingLogHandler.log;
|
|
||||||
if (w != null) {
|
|
||||||
val f = new XMLFormatter()
|
|
||||||
val out = f.format(record)
|
|
||||||
w.write(out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,17 +73,4 @@ object JavaLoggingLogHandler {
|
|||||||
case LogLevel.Debug => Level.FINE
|
case LogLevel.Debug => Level.FINE
|
||||||
case LogLevel.Trace => Level.ALL
|
case LogLevel.Trace => Level.ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
var log: Writer = null
|
|
||||||
def registerLogFile(file: File): Unit = {
|
|
||||||
if (this.log != null) {
|
|
||||||
this.log.close()
|
|
||||||
}
|
|
||||||
val w = new FileWriter(file, StandardCharsets.UTF_8, false)
|
|
||||||
w.write(
|
|
||||||
"<?xml version='1.0' encoding='UTF-8'?><uigestures version='1.0'>\n"
|
|
||||||
)
|
|
||||||
this.log = w
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -132,4 +132,16 @@ object LogLevel {
|
|||||||
case Debug => akka.event.Logging.DebugLevel
|
case Debug => akka.event.Logging.DebugLevel
|
||||||
case Trace => akka.event.Logging.DebugLevel
|
case Trace => akka.event.Logging.DebugLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Converts our internal [[LogLevel]] to the corresponding instance of
|
||||||
|
* Java log level.
|
||||||
|
*/
|
||||||
|
def toJava(logLevel: LogLevel): java.util.logging.Level = logLevel match {
|
||||||
|
case Off => java.util.logging.Level.OFF
|
||||||
|
case Error => java.util.logging.Level.SEVERE
|
||||||
|
case Warning => java.util.logging.Level.WARNING
|
||||||
|
case Info => java.util.logging.Level.INFO
|
||||||
|
case Debug => java.util.logging.Level.FINER
|
||||||
|
case Trace => java.util.logging.Level.FINEST
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
package org.enso.loggingservice
|
package org.enso.loggingservice
|
||||||
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
import akka.http.scaladsl.model.Uri
|
import akka.http.scaladsl.model.Uri
|
||||||
import com.typesafe.scalalogging.Logger
|
import com.typesafe.scalalogging.Logger
|
||||||
import org.enso.logger.masking.Masking
|
import org.enso.logger.masking.Masking
|
||||||
import org.enso.loggingservice.printers.{
|
import org.enso.loggingservice.printers._
|
||||||
FileOutputPrinter,
|
|
||||||
Printer,
|
import java.nio.file.Path
|
||||||
StderrPrinter,
|
|
||||||
StderrPrinterWithColors
|
|
||||||
}
|
|
||||||
|
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
|
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
|
||||||
@ -54,7 +49,8 @@ abstract class LoggingServiceSetupHelper(implicit
|
|||||||
logLevel: Option[LogLevel],
|
logLevel: Option[LogLevel],
|
||||||
connectToExternalLogger: Option[Uri],
|
connectToExternalLogger: Option[Uri],
|
||||||
colorMode: ColorMode,
|
colorMode: ColorMode,
|
||||||
logMasking: Boolean
|
logMasking: Boolean,
|
||||||
|
profilingLog: Option[Path]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val actualLogLevel = logLevel.getOrElse(defaultLogLevel)
|
val actualLogLevel = logLevel.getOrElse(defaultLogLevel)
|
||||||
Masking.setup(logMasking)
|
Masking.setup(logMasking)
|
||||||
@ -62,7 +58,7 @@ abstract class LoggingServiceSetupHelper(implicit
|
|||||||
case Some(uri) =>
|
case Some(uri) =>
|
||||||
setupLoggingConnection(uri, actualLogLevel)
|
setupLoggingConnection(uri, actualLogLevel)
|
||||||
case None =>
|
case None =>
|
||||||
setupLoggingServer(actualLogLevel, colorMode)
|
setupLoggingServer(actualLogLevel, colorMode, profilingLog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +110,8 @@ abstract class LoggingServiceSetupHelper(implicit
|
|||||||
|
|
||||||
private def setupLoggingServer(
|
private def setupLoggingServer(
|
||||||
logLevel: LogLevel,
|
logLevel: LogLevel,
|
||||||
colorMode: ColorMode
|
colorMode: ColorMode,
|
||||||
|
profilingLog: Option[Path]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val printExceptionsInStderr =
|
val printExceptionsInStderr =
|
||||||
implicitly[Ordering[LogLevel]].compare(logLevel, LogLevel.Debug) >= 0
|
implicitly[Ordering[LogLevel]].compare(logLevel, LogLevel.Debug) >= 0
|
||||||
@ -132,10 +129,11 @@ abstract class LoggingServiceSetupHelper(implicit
|
|||||||
suffix = logFileSuffix,
|
suffix = logFileSuffix,
|
||||||
printExceptions = true
|
printExceptions = true
|
||||||
)
|
)
|
||||||
|
val profilingPrinterOpt = profilingLog.map(new FileXmlPrinter(_))
|
||||||
Seq(
|
Seq(
|
||||||
stderrPrinter(colorMode, printExceptionsInStderr),
|
stderrPrinter(colorMode, printExceptionsInStderr),
|
||||||
filePrinter
|
filePrinter
|
||||||
)
|
) ++ profilingPrinterOpt
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(error) =>
|
case NonFatal(error) =>
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@ -70,7 +70,7 @@ object FileOutputPrinter {
|
|||||||
def create(
|
def create(
|
||||||
logDirectory: Path,
|
logDirectory: Path,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
printExceptions: Boolean = true
|
printExceptions: Boolean
|
||||||
): FileOutputPrinter =
|
): FileOutputPrinter =
|
||||||
new FileOutputPrinter(logDirectory, suffix, printExceptions)
|
new FileOutputPrinter(logDirectory, suffix, printExceptions)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package org.enso.loggingservice.printers
|
||||||
|
|
||||||
|
import org.enso.loggingservice.LogLevel
|
||||||
|
import org.enso.loggingservice.internal.protocol.WSLogMessage
|
||||||
|
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.nio.file.{Files, Path, StandardOpenOption}
|
||||||
|
import java.util.logging.{LogRecord, XMLFormatter}
|
||||||
|
|
||||||
|
/** Creates a new file in [[logPath]] and writes incoming log messages to
|
||||||
|
* this file in XML format.
|
||||||
|
*
|
||||||
|
* @param logPath the file path to log
|
||||||
|
*/
|
||||||
|
class FileXmlPrinter(logPath: Path) extends Printer {
|
||||||
|
|
||||||
|
private val writer = initializeWriter()
|
||||||
|
private val formatter = new XMLFormatter()
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def print(message: WSLogMessage): Unit = {
|
||||||
|
val lines = formatter.format(toLogRecord(message))
|
||||||
|
writer.print(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
override def shutdown(): Unit = {
|
||||||
|
writer.flush()
|
||||||
|
writer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Opens the log file for writing. */
|
||||||
|
private def initializeWriter(): PrintWriter = {
|
||||||
|
Option(logPath.getParent).foreach(Files.createDirectories(_))
|
||||||
|
val writer = new PrintWriter(
|
||||||
|
Files.newBufferedWriter(
|
||||||
|
logPath,
|
||||||
|
StandardOpenOption.CREATE,
|
||||||
|
StandardOpenOption.TRUNCATE_EXISTING,
|
||||||
|
StandardOpenOption.WRITE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
writer.println(FileXmlPrinter.Header)
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts [[WSLogMessage]] to java [[LogRecord]]. */
|
||||||
|
private def toLogRecord(wsLogMessage: WSLogMessage): LogRecord = {
|
||||||
|
val record =
|
||||||
|
new LogRecord(LogLevel.toJava(wsLogMessage.level), wsLogMessage.message)
|
||||||
|
record.setInstant(wsLogMessage.timestamp)
|
||||||
|
record.setLoggerName(wsLogMessage.group)
|
||||||
|
record
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
object FileXmlPrinter {
|
||||||
|
|
||||||
|
private val Header: String =
|
||||||
|
"<?xml version='1.0' encoding='UTF-8'?><uigestures version='1.0'>"
|
||||||
|
}
|
@ -3,10 +3,9 @@ package org.enso.profiling
|
|||||||
import org.netbeans.modules.sampler.Sampler
|
import org.netbeans.modules.sampler.Sampler
|
||||||
|
|
||||||
import java.io.{DataOutputStream, File, FileOutputStream}
|
import java.io.{DataOutputStream, File, FileOutputStream}
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.concurrent.{CompletableFuture, Executor, Executors}
|
import java.util.concurrent.{CompletableFuture, Executor, Executors}
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.util.Using
|
import scala.util.Using
|
||||||
|
|
||||||
/** Gathers application performance statistics that can be visualised in Java
|
/** Gathers application performance statistics that can be visualised in Java
|
||||||
@ -14,16 +13,11 @@ import scala.util.Using
|
|||||||
*
|
*
|
||||||
* @param output the output stream to write the collected statistics to
|
* @param output the output stream to write the collected statistics to
|
||||||
*/
|
*/
|
||||||
final class TempFileSampler(output: File) extends MethodsSampler {
|
final class FileSampler(output: File) extends MethodsSampler {
|
||||||
|
|
||||||
private val sampler: Sampler = Sampler.createSampler(getClass.getSimpleName)
|
private val sampler: Sampler = Sampler.createSampler(getClass.getSimpleName)
|
||||||
private var samplingStarted: Boolean = false
|
private var samplingStarted: Boolean = false
|
||||||
|
|
||||||
def getSiblingFile(ext: String): File = {
|
|
||||||
val newName = output.getName.replace(".npss", ext)
|
|
||||||
new File(output.getParent, newName)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
def start(): Unit =
|
def start(): Unit =
|
||||||
this.synchronized {
|
this.synchronized {
|
||||||
@ -45,7 +39,7 @@ final class TempFileSampler(output: File) extends MethodsSampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
def stop(delay: Duration)(implicit ec: Executor): Unit =
|
def stop(delay: FiniteDuration)(implicit ec: Executor): Unit =
|
||||||
this.synchronized {
|
this.synchronized {
|
||||||
val executor = Executors.newSingleThreadScheduledExecutor()
|
val executor = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
|
||||||
@ -58,22 +52,4 @@ final class TempFileSampler(output: File) extends MethodsSampler {
|
|||||||
)
|
)
|
||||||
.whenComplete((_, _) => executor.shutdown())
|
.whenComplete((_, _) => executor.shutdown())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return `true` if the sampling is started. */
|
|
||||||
def isSamplingStarted: Boolean =
|
|
||||||
this.samplingStarted
|
|
||||||
}
|
|
||||||
object TempFileSampler {
|
|
||||||
|
|
||||||
/** Create an instance of [[MethodsSampler]] that writes the data to the
|
|
||||||
* temporary `.npss` file with the provided prefix.
|
|
||||||
*
|
|
||||||
* @param prefix the prefix of the temp file.
|
|
||||||
* @return the [[MethodsSampler]] instance
|
|
||||||
*/
|
|
||||||
def apply(prefix: String): TempFileSampler = {
|
|
||||||
val path = Files.createTempFile(s"$prefix-", ".npss")
|
|
||||||
Files.deleteIfExists(path)
|
|
||||||
new TempFileSampler(path.toFile)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package org.enso.profiling
|
|||||||
|
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/** Sampler gathers the application performance statistics. */
|
/** Sampler gathers the application performance statistics. */
|
||||||
trait MethodsSampler {
|
trait MethodsSampler {
|
||||||
@ -19,5 +19,5 @@ trait MethodsSampler {
|
|||||||
* @param delay the duration to wait before stopping
|
* @param delay the duration to wait before stopping
|
||||||
* @param ec the execution context
|
* @param ec the execution context
|
||||||
*/
|
*/
|
||||||
def stop(delay: Duration)(implicit ec: Executor): Unit
|
def stop(delay: FiniteDuration)(implicit ec: Executor): Unit
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package org.enso.profiling
|
|||||||
|
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/** Sampler that does nothing. */
|
/** Sampler that does nothing. */
|
||||||
final class NoopSampler extends MethodsSampler {
|
final class NoopSampler extends MethodsSampler {
|
||||||
@ -14,7 +14,7 @@ final class NoopSampler extends MethodsSampler {
|
|||||||
override def stop(): Unit = ()
|
override def stop(): Unit = ()
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def stop(delay: Duration)(implicit ec: Executor): Unit = ()
|
override def stop(delay: FiniteDuration)(implicit ec: Executor): Unit = ()
|
||||||
}
|
}
|
||||||
object NoopSampler {
|
object NoopSampler {
|
||||||
|
|
||||||
|
@ -7,12 +7,14 @@ import scala.util.Try
|
|||||||
|
|
||||||
object Cli {
|
object Cli {
|
||||||
|
|
||||||
val JSON_OPTION = "json"
|
val JSON_OPTION = "json"
|
||||||
val HELP_OPTION = "help"
|
val HELP_OPTION = "help"
|
||||||
val NO_LOG_MASKING = "no-log-masking"
|
val NO_LOG_MASKING = "no-log-masking"
|
||||||
val VERBOSE_OPTION = "verbose"
|
val VERBOSE_OPTION = "verbose"
|
||||||
val VERSION_OPTION = "version"
|
val VERSION_OPTION = "version"
|
||||||
val ENABLE_PROFILING = "profiling"
|
val PROFILING_PATH = "profiling-path"
|
||||||
|
val PROFILING_TIME = "profiling-time"
|
||||||
|
val PROFILING_EVENTS_LOG_PATH = "profiling-events-log-path"
|
||||||
|
|
||||||
object option {
|
object option {
|
||||||
|
|
||||||
@ -47,9 +49,30 @@ object Cli {
|
|||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val enableProfiling: cli.Option = cli.Option.builder
|
val profilingPath: cli.Option = cli.Option.builder
|
||||||
.longOpt(ENABLE_PROFILING)
|
.hasArg(true)
|
||||||
.desc("Enables the application profiling.")
|
.numberOfArgs(1)
|
||||||
|
.argName("file")
|
||||||
|
.longOpt(PROFILING_PATH)
|
||||||
|
.desc("The path to profiling file. Enables the application profiling.")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val profilingTime: cli.Option = cli.Option.builder
|
||||||
|
.hasArg(true)
|
||||||
|
.numberOfArgs(1)
|
||||||
|
.argName("seconds")
|
||||||
|
.longOpt(PROFILING_TIME)
|
||||||
|
.desc("The duration in seconds limiting the application profiling time.")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val profilingEventsLogPath: cli.Option = cli.Option.builder
|
||||||
|
.hasArg(true)
|
||||||
|
.numberOfArgs(1)
|
||||||
|
.argName("file")
|
||||||
|
.longOpt(PROFILING_EVENTS_LOG_PATH)
|
||||||
|
.desc(
|
||||||
|
"The path to the runtime events log file. Enables the runtime events logging."
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +83,9 @@ object Cli {
|
|||||||
.addOption(option.version)
|
.addOption(option.version)
|
||||||
.addOption(option.json)
|
.addOption(option.json)
|
||||||
.addOption(option.noLogMasking)
|
.addOption(option.noLogMasking)
|
||||||
.addOption(option.enableProfiling)
|
.addOption(option.profilingPath)
|
||||||
|
.addOption(option.profilingTime)
|
||||||
|
.addOption(option.profilingEventsLogPath)
|
||||||
|
|
||||||
/** Parse the command line options. */
|
/** Parse the command line options. */
|
||||||
def parse(args: Array[String]): Either[String, cli.CommandLine] = {
|
def parse(args: Array[String]): Either[String, cli.CommandLine] = {
|
||||||
|
@ -23,6 +23,7 @@ import zio.console._
|
|||||||
import zio.interop.catz.core._
|
import zio.interop.catz.core._
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths}
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor
|
import java.util.concurrent.ScheduledThreadPoolExecutor
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
@ -120,23 +121,99 @@ object ProjectManager extends App with LazyLogging {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Parses and validates the command line arguments.
|
||||||
|
*
|
||||||
|
* @param options the command line arguments
|
||||||
|
*/
|
||||||
|
def parseOpts(
|
||||||
|
options: CommandLine
|
||||||
|
): ZIO[ZEnv, Throwable, ProjectManagerOptions] = {
|
||||||
|
val parseProfilingPath = ZIO
|
||||||
|
.effect {
|
||||||
|
Option(options.getOptionValue(Cli.PROFILING_PATH))
|
||||||
|
.map(Paths.get(_).toAbsolutePath)
|
||||||
|
}
|
||||||
|
.flatMap {
|
||||||
|
case pathOpt @ Some(path) =>
|
||||||
|
ZIO.ifM(ZIO.effect(Files.isDirectory(path)))(
|
||||||
|
onTrue = putStrLnErr(
|
||||||
|
s"Error: ${Cli.PROFILING_PATH} is a directory: $path"
|
||||||
|
) *>
|
||||||
|
ZIO.fail(new FileAlreadyExistsException(path.toString)),
|
||||||
|
onFalse = ZIO.succeed(pathOpt)
|
||||||
|
)
|
||||||
|
case None =>
|
||||||
|
ZIO.succeed(None)
|
||||||
|
}
|
||||||
|
.catchAll { err =>
|
||||||
|
putStrLnErr(s"Invalid ${Cli.PROFILING_PATH} argument.") *> ZIO.fail(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parseProfilingTime = ZIO
|
||||||
|
.effect {
|
||||||
|
Option(options.getOptionValue(Cli.PROFILING_TIME))
|
||||||
|
.map(_.toInt.seconds)
|
||||||
|
}
|
||||||
|
.catchAll { err =>
|
||||||
|
putStrLnErr(s"Invalid ${Cli.PROFILING_TIME} argument.") *> ZIO.fail(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parseProfilingEventsLogPath = ZIO
|
||||||
|
.effect {
|
||||||
|
Option(options.getOptionValue(Cli.PROFILING_EVENTS_LOG_PATH))
|
||||||
|
.map(Paths.get(_).toAbsolutePath)
|
||||||
|
}
|
||||||
|
.flatMap {
|
||||||
|
case pathOpt @ Some(path) =>
|
||||||
|
ZIO.ifM(ZIO.effect(Files.isDirectory(path)))(
|
||||||
|
onTrue = putStrLnErr(
|
||||||
|
s"Error: ${Cli.PROFILING_EVENTS_LOG_PATH} is a directory: $path"
|
||||||
|
) *>
|
||||||
|
ZIO.fail(new FileAlreadyExistsException(path.toString)),
|
||||||
|
onFalse = ZIO.succeed(pathOpt)
|
||||||
|
)
|
||||||
|
case None =>
|
||||||
|
ZIO.succeed(None)
|
||||||
|
}
|
||||||
|
.catchAll { err =>
|
||||||
|
putStrLnErr(s"Invalid ${Cli.PROFILING_EVENTS_LOG_PATH} argument.") *>
|
||||||
|
ZIO.fail(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
profilingEventsLogPath <- parseProfilingEventsLogPath
|
||||||
|
profilingPath <- parseProfilingPath
|
||||||
|
profilingTime <- parseProfilingTime
|
||||||
|
} yield ProjectManagerOptions(
|
||||||
|
profilingEventsLogPath,
|
||||||
|
profilingPath,
|
||||||
|
profilingTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** The main function of the application, which will be passed the command-line
|
/** The main function of the application, which will be passed the command-line
|
||||||
* arguments to the program and has to return an `IO` with the errors fully handled.
|
* arguments to the program and has to return an `IO` with the errors fully handled.
|
||||||
*/
|
*/
|
||||||
def runOpts(options: CommandLine): ZIO[ZEnv, IOException, ExitCode] = {
|
def runOpts(options: CommandLine): ZIO[ZEnv, Throwable, ExitCode] = {
|
||||||
if (options.hasOption(Cli.HELP_OPTION)) {
|
if (options.hasOption(Cli.HELP_OPTION)) {
|
||||||
ZIO.effectTotal(Cli.printHelp()) *>
|
ZIO.effectTotal(Cli.printHelp()) *>
|
||||||
ZIO.succeed(SuccessExitCode)
|
ZIO.succeed(SuccessExitCode)
|
||||||
} else if (options.hasOption(Cli.VERSION_OPTION)) {
|
} else if (options.hasOption(Cli.VERSION_OPTION)) {
|
||||||
displayVersion(options.hasOption(Cli.JSON_OPTION))
|
displayVersion(options.hasOption(Cli.JSON_OPTION))
|
||||||
} else {
|
} else {
|
||||||
val verbosity = options.getOptions.count(_ == Cli.option.verbose)
|
val verbosity = options.getOptions.count(_ == Cli.option.verbose)
|
||||||
val logMasking = !options.hasOption(Cli.NO_LOG_MASKING)
|
val logMasking = !options.hasOption(Cli.NO_LOG_MASKING)
|
||||||
val profilingEnabled = options.hasOption(Cli.ENABLE_PROFILING)
|
|
||||||
logger.info("Starting Project Manager...")
|
logger.info("Starting Project Manager...")
|
||||||
for {
|
for {
|
||||||
logLevel <- setupLogging(verbosity, logMasking)
|
opts <- parseOpts(options)
|
||||||
procConf = MainProcessConfig(logLevel, profilingEnabled)
|
profilingLog = opts.profilingPath.map(getSiblingFile(_, ".log"))
|
||||||
|
logLevel <- setupLogging(verbosity, logMasking, profilingLog)
|
||||||
|
procConf = MainProcessConfig(
|
||||||
|
logLevel,
|
||||||
|
opts.profilingRuntimeEventsLog,
|
||||||
|
opts.profilingPath,
|
||||||
|
opts.profilingTime
|
||||||
|
)
|
||||||
exitCode <- mainProcess(procConf).fold(
|
exitCode <- mainProcess(procConf).fold(
|
||||||
th => {
|
th => {
|
||||||
logger.error("Main process execution failed.", th)
|
logger.error("Main process execution failed.", th)
|
||||||
@ -150,7 +227,8 @@ object ProjectManager extends App with LazyLogging {
|
|||||||
|
|
||||||
private def setupLogging(
|
private def setupLogging(
|
||||||
verbosityLevel: Int,
|
verbosityLevel: Int,
|
||||||
logMasking: Boolean
|
logMasking: Boolean,
|
||||||
|
profilingLog: Option[Path]
|
||||||
): ZIO[Console, IOException, LogLevel] = {
|
): ZIO[Console, IOException, LogLevel] = {
|
||||||
val level = verbosityLevel match {
|
val level = verbosityLevel match {
|
||||||
case 0 => LogLevel.Info
|
case 0 => LogLevel.Info
|
||||||
@ -164,7 +242,7 @@ object ProjectManager extends App with LazyLogging {
|
|||||||
|
|
||||||
ZIO
|
ZIO
|
||||||
.effect {
|
.effect {
|
||||||
Logging.setup(Some(level), None, colorMode, logMasking)
|
Logging.setup(Some(level), None, colorMode, logMasking, profilingLog)
|
||||||
}
|
}
|
||||||
.catchAll { exception =>
|
.catchAll { exception =>
|
||||||
putStrLnErr(s"Failed to setup the logger: $exception")
|
putStrLnErr(s"Failed to setup the logger: $exception")
|
||||||
@ -203,4 +281,12 @@ object ProjectManager extends App with LazyLogging {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getSiblingFile(file: Path, ext: String): Path = {
|
||||||
|
val fileName = file.getFileName.toString
|
||||||
|
val extensionIndex = fileName.lastIndexOf(".")
|
||||||
|
val newName =
|
||||||
|
if (extensionIndex > 0) fileName.substring(0, extensionIndex) + ext
|
||||||
|
else fileName + ext
|
||||||
|
file.getParent.resolve(newName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.enso.projectmanager.boot
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
/** The runtime options.
|
||||||
|
*
|
||||||
|
* @param profilingRuntimeEventsLog the path to the runtime events log file
|
||||||
|
* @param profilingPath the path to the profiling output file
|
||||||
|
* @param profilingTime the time limiting the profiling duration
|
||||||
|
*/
|
||||||
|
case class ProjectManagerOptions(
|
||||||
|
profilingRuntimeEventsLog: Option[Path],
|
||||||
|
profilingPath: Option[Path],
|
||||||
|
profilingTime: Option[FiniteDuration]
|
||||||
|
)
|
@ -3,6 +3,7 @@ package org.enso.projectmanager.boot
|
|||||||
import org.enso.loggingservice.LogLevel
|
import org.enso.loggingservice.LogLevel
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
@ -12,11 +13,15 @@ object configuration {
|
|||||||
* main project manager process.
|
* main project manager process.
|
||||||
*
|
*
|
||||||
* @param logLevel the logging level
|
* @param logLevel the logging level
|
||||||
* @param profilingEnabled if the profiling is enabled
|
* @param profilingEventsLogPath the path to the runtime events log file
|
||||||
|
* @param profilingPath the path to the profiling out file
|
||||||
|
* @param profilingTime the time limiting the profiling duration
|
||||||
*/
|
*/
|
||||||
case class MainProcessConfig(
|
case class MainProcessConfig(
|
||||||
logLevel: LogLevel,
|
logLevel: LogLevel,
|
||||||
profilingEnabled: Boolean
|
profilingEventsLogPath: Option[Path],
|
||||||
|
profilingPath: Option[Path],
|
||||||
|
profilingTime: Option[FiniteDuration]
|
||||||
)
|
)
|
||||||
|
|
||||||
/** A configuration object for properties of the Project Manager.
|
/** A configuration object for properties of the Project Manager.
|
||||||
|
@ -95,8 +95,23 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
|
|||||||
environment = distributionConfiguration.environment,
|
environment = distributionConfiguration.environment,
|
||||||
loggerConnection = descriptor.deferredLoggingServiceEndpoint
|
loggerConnection = descriptor.deferredLoggingServiceEndpoint
|
||||||
)
|
)
|
||||||
|
val profilingPathArguments =
|
||||||
|
descriptor.profilingPath.toSeq
|
||||||
|
.flatMap(path => Seq("--server-profiling-path", path.toString))
|
||||||
|
val profilingTimeArguments =
|
||||||
|
descriptor.profilingTime.toSeq
|
||||||
|
.flatMap(time =>
|
||||||
|
Seq("--server-profiling-time", time.toSeconds.toString)
|
||||||
|
)
|
||||||
|
val profilingEventsLogPathArguments =
|
||||||
|
descriptor.profilingEventsLogPath.toSeq
|
||||||
|
.flatMap(path =>
|
||||||
|
Seq("--server-profiling-events-log-path", path.toString)
|
||||||
|
)
|
||||||
val additionalArguments =
|
val additionalArguments =
|
||||||
Option.when(descriptor.profilingEnabled)("--server-profiling").toSeq
|
profilingPathArguments ++
|
||||||
|
profilingTimeArguments ++
|
||||||
|
profilingEventsLogPathArguments
|
||||||
val runSettings = runner
|
val runSettings = runner
|
||||||
.startLanguageServer(
|
.startLanguageServer(
|
||||||
options = options,
|
options = options,
|
||||||
|
@ -79,7 +79,9 @@ class LanguageServerController(
|
|||||||
engineVersion = engineVersion,
|
engineVersion = engineVersion,
|
||||||
jvmSettings = distributionConfiguration.defaultJVMSettings,
|
jvmSettings = distributionConfiguration.defaultJVMSettings,
|
||||||
discardOutput = distributionConfiguration.shouldDiscardChildOutput,
|
discardOutput = distributionConfiguration.shouldDiscardChildOutput,
|
||||||
profilingEnabled = processConfig.profilingEnabled,
|
profilingEventsLogPath = processConfig.profilingEventsLogPath,
|
||||||
|
profilingPath = processConfig.profilingPath,
|
||||||
|
profilingTime = processConfig.profilingTime,
|
||||||
deferredLoggingServiceEndpoint = loggingServiceDescriptor.getEndpoint
|
deferredLoggingServiceEndpoint = loggingServiceDescriptor.getEndpoint
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package org.enso.projectmanager.infrastructure.languageserver
|
package org.enso.projectmanager.infrastructure.languageserver
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
import akka.http.scaladsl.model.Uri
|
import akka.http.scaladsl.model.Uri
|
||||||
import nl.gn0s1s.bump.SemVer
|
import nl.gn0s1s.bump.SemVer
|
||||||
import org.enso.projectmanager.boot.configuration.NetworkConfig
|
import org.enso.projectmanager.boot.configuration.NetworkConfig
|
||||||
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
import org.enso.projectmanager.versionmanagement.DistributionConfiguration
|
||||||
import org.enso.runtimeversionmanager.runner.JVMSettings
|
import org.enso.runtimeversionmanager.runner.JVMSettings
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
/** A descriptor specifying options related to starting a Language Server.
|
/** A descriptor specifying options related to starting a Language Server.
|
||||||
*
|
*
|
||||||
@ -23,7 +25,9 @@ import scala.concurrent.Future
|
|||||||
* @param jvmSettings settings to use for the JVM that will host the engine
|
* @param jvmSettings settings to use for the JVM that will host the engine
|
||||||
* @param discardOutput specifies if the process output should be discarded or
|
* @param discardOutput specifies if the process output should be discarded or
|
||||||
* printed to parent's streams
|
* printed to parent's streams
|
||||||
* @param profilingEnabled enables the language server profiling
|
* @param profilingEventsLogPath the path to the runtime events log file
|
||||||
|
* @param profilingPath the language server profiling file path
|
||||||
|
* @param profilingTime the time limiting the profiling duration
|
||||||
* @param deferredLoggingServiceEndpoint a future that is completed once the
|
* @param deferredLoggingServiceEndpoint a future that is completed once the
|
||||||
* logging service has been fully set-up;
|
* logging service has been fully set-up;
|
||||||
* if the child component should connect
|
* if the child component should connect
|
||||||
@ -39,6 +43,8 @@ case class LanguageServerDescriptor(
|
|||||||
engineVersion: SemVer,
|
engineVersion: SemVer,
|
||||||
jvmSettings: JVMSettings,
|
jvmSettings: JVMSettings,
|
||||||
discardOutput: Boolean,
|
discardOutput: Boolean,
|
||||||
profilingEnabled: Boolean,
|
profilingEventsLogPath: Option[Path],
|
||||||
|
profilingPath: Option[Path],
|
||||||
|
profilingTime: Option[FiniteDuration],
|
||||||
deferredLoggingServiceEndpoint: Future[Option[Uri]]
|
deferredLoggingServiceEndpoint: Future[Option[Uri]]
|
||||||
)
|
)
|
||||||
|
@ -71,7 +71,7 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll {
|
|||||||
val debugLogs: Boolean = false
|
val debugLogs: Boolean = false
|
||||||
|
|
||||||
/** Enables the application profiling. */
|
/** Enables the application profiling. */
|
||||||
val profilingEnabled: Boolean = false
|
val profilingPath: Option[Path] = None
|
||||||
|
|
||||||
/** Tests can override this to allow child process output to be displayed. */
|
/** Tests can override this to allow child process output to be displayed. */
|
||||||
val debugChildLogs: Boolean = false
|
val debugChildLogs: Boolean = false
|
||||||
@ -87,8 +87,10 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll {
|
|||||||
|
|
||||||
val processConfig: MainProcessConfig =
|
val processConfig: MainProcessConfig =
|
||||||
MainProcessConfig(
|
MainProcessConfig(
|
||||||
logLevel = if (debugLogs) LogLevel.Trace else LogLevel.Off,
|
logLevel = if (debugLogs) LogLevel.Trace else LogLevel.Off,
|
||||||
profilingEnabled = profilingEnabled
|
profilingPath = profilingPath,
|
||||||
|
profilingTime = None,
|
||||||
|
profilingEventsLogPath = None
|
||||||
)
|
)
|
||||||
|
|
||||||
val testClock =
|
val testClock =
|
||||||
|
Loading…
Reference in New Issue
Block a user