From 033ccd5aecf004dc69a37e36e3d32e17de29cb43 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 9 Sep 2020 18:16:59 +0300 Subject: [PATCH] Setup Runtime logging (#1128) Configure logging of the Runtime context. --- .../enso/languageserver/boot/MainModule.scala | 12 ++ .../enso/languageserver/util/LogHandler.scala | 41 ++++++ .../enso/languageserver/util/Logging.scala | 125 ++++++++++++++++++ .../projectmanager/boot/ProjectManager.scala | 2 +- .../enso/projectmanager/util/Logging.scala | 66 --------- 5 files changed, 179 insertions(+), 67 deletions(-) create mode 100644 engine/language-server/src/main/scala/org/enso/languageserver/util/LogHandler.scala create mode 100644 engine/language-server/src/main/scala/org/enso/languageserver/util/Logging.scala delete mode 100644 lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/Logging.scala diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala index ee402644b87..69c89dfaeae 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/MainModule.scala @@ -2,6 +2,7 @@ package org.enso.languageserver.boot import java.io.File import java.net.URI +import java.util.logging.ConsoleHandler import akka.actor.ActorSystem import org.enso.jsonrpc.JsonRpcServer @@ -28,6 +29,7 @@ import org.enso.languageserver.runtime._ import org.enso.languageserver.search.SuggestionsHandler import org.enso.languageserver.session.SessionRouter import org.enso.languageserver.text.BufferRegistry +import org.enso.languageserver.util.Logging import org.enso.languageserver.util.binary.BinaryEncoder import org.enso.polyglot.{LanguageInfo, RuntimeOptions, RuntimeServerInfo} import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo} @@ -148,12 +150,21 @@ class MainModule(serverConfig: LanguageServerConfig) { val stdIn = new ObservablePipedInputStream(stdInSink) log.trace("Initializing Runtime context...") + val logHandler = Logging.getLogHandler(LanguageInfo.ID) match { + case Left(t) => + log.warn("Failed to create the Runtime logger", t) + new ConsoleHandler() + case Right(handler) => + log.trace(s"Setting Runtime logger") + handler + } val context = Context .newBuilder(LanguageInfo.ID) .allowAllAccess(true) .allowExperimentalOptions(true) .option(RuntimeServerInfo.ENABLE_OPTION, "true") .option(RuntimeOptions.PACKAGES_PATH, serverConfig.contentRootPath) + .option(RuntimeOptions.LOG_LEVEL, logHandler.getLevel.toString) .option( RuntimeServerInfo.JOB_PARALLELISM_OPTION, Runtime.getRuntime.availableProcessors().toString @@ -161,6 +172,7 @@ class MainModule(serverConfig: LanguageServerConfig) { .out(stdOut) .err(stdErr) .in(stdIn) + .logHandler(logHandler) .serverTransport((uri: URI, peerEndpoint: MessageEndpoint) => { if (uri.toString == RuntimeServerInfo.URI) { val connection = new RuntimeConnector.Endpoint( diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/util/LogHandler.scala b/engine/language-server/src/main/scala/org/enso/languageserver/util/LogHandler.scala new file mode 100644 index 00000000000..a75f56b0eda --- /dev/null +++ b/engine/language-server/src/main/scala/org/enso/languageserver/util/LogHandler.scala @@ -0,0 +1,41 @@ +package org.enso.languageserver.util + +import java.util.logging.{Handler, Level, LogRecord} + +import ch.qos.logback.classic +import org.slf4j.event + +class LogHandler(logger: classic.Logger) extends Handler { + + private val level = logger.getLevel + + /** @inheritdoc */ + override def publish(record: LogRecord): Unit = { + logger.log( + null, + record.getSourceClassName, + LogHandler.toSlf4j(record.getLevel).toInt, + record.getMessage, + record.getParameters, + record.getThrown + ) + } + + /** @inheritdoc */ + override def flush(): Unit = () + + /** @inheritdoc */ + override def close(): Unit = () + + /** @inheritdoc */ + override def getLevel: Level = + Logging.LogLevel.toJava(Logging.LogLevel.fromLogback(level)) + +} + +object LogHandler { + + /** Convert java utils log level to slf4j. */ + private def toSlf4j(level: Level): event.Level = + Logging.LogLevel.toSlf4j(Logging.LogLevel.fromJava(level)) +} diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/util/Logging.scala b/engine/language-server/src/main/scala/org/enso/languageserver/util/Logging.scala new file mode 100644 index 00000000000..bca674a7b50 --- /dev/null +++ b/engine/language-server/src/main/scala/org/enso/languageserver/util/Logging.scala @@ -0,0 +1,125 @@ +package org.enso.languageserver.util + +import java.util + +import cats.syntax.either._ +import ch.qos.logback.classic.{Level, Logger, LoggerContext} +import org.slf4j.{event, LoggerFactory} + +object Logging { + + /** Application log level. */ + sealed trait LogLevel + object LogLevel { + + case object Error extends LogLevel + case object Warning extends LogLevel + case object Info extends LogLevel + case object Debug extends LogLevel + case object Trace extends LogLevel + + /** Convert to logback log level. */ + def toLogback(level: LogLevel): Level = + level match { + case Error => Level.ERROR + case Warning => Level.WARN + case Info => Level.INFO + case Debug => Level.DEBUG + case Trace => Level.TRACE + } + + /** Convert from logback log level. */ + def fromLogback(level: Level): LogLevel = { + level match { + case Level.`ERROR` => Error + case Level.`WARN` => Warning + case Level.`INFO` => Info + case Level.`DEBUG` => Debug + case Level.`TRACE` => Trace + } + } + + /** Convert to java util logging level. */ + def toJava(level: LogLevel): util.logging.Level = + level match { + case Error => util.logging.Level.SEVERE + case Warning => util.logging.Level.WARNING + case Info => util.logging.Level.INFO + case Debug => util.logging.Level.FINE + case Trace => util.logging.Level.FINEST + } + + /** Convert from java util logging level. */ + def fromJava(level: util.logging.Level): LogLevel = + level match { + case util.logging.Level.`SEVERE` => LogLevel.Error + case util.logging.Level.`WARNING` => LogLevel.Warning + case util.logging.Level.`INFO` => LogLevel.Info + case util.logging.Level.`CONFIG` => LogLevel.Debug + case util.logging.Level.`FINE` => LogLevel.Debug + case util.logging.Level.`FINER` => LogLevel.Debug + case util.logging.Level.`FINEST` => LogLevel.Trace + } + + /** Convert to slf4j logging level. */ + def toSlf4j(level: LogLevel): event.Level = + level match { + case Error => event.Level.ERROR + case Warning => event.Level.WARN + case Info => event.Level.INFO + case Debug => event.Level.DEBUG + case Trace => event.Level.TRACE + } + } + + private val ROOT_LOGGER = "org.enso" + + /** + * Set log level for the application root logger. + * + * @param level the log level + * @return the new log level + */ + def setLogLevel(level: LogLevel): Either[Throwable, LogLevel] = { + Either.catchNonFatal { + val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + ctx.getLogger(ROOT_LOGGER).setLevel(LogLevel.toLogback(level)) + level + } + } + + /** Get log level of the application root logger. */ + def getLogLevel: Either[Throwable, LogLevel] = { + Either.catchNonFatal { + val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + val level = ctx.getLogger(ROOT_LOGGER).getLevel + LogLevel.fromLogback(level) + } + } + + /** Get the application logger. + * + * @param name the logger name + * @return the application logger + */ + def getLogger(name: String): Either[Throwable, Logger] = { + Either.catchNonFatal { + val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] + ctx.getLogger(name) + } + } + + /** Get the java log handler instance backed by the application logger. + * + * @param name the logger name + * @return the application log handler + */ + def getLogHandler(name: String): Either[Throwable, LogHandler] = + for { + level <- Logging.getLogLevel + logger <- Logging.getLogger(name) + } yield { + logger.setLevel(Logging.LogLevel.toLogback(level)) + new LogHandler(logger) + } +} diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala index 470924755f3..a973ac4e388 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/ProjectManager.scala @@ -6,6 +6,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor import akka.http.scaladsl.Http import com.typesafe.scalalogging.LazyLogging import org.apache.commons.cli.CommandLine +import org.enso.languageserver.util.Logging import org.enso.projectmanager.boot.Globals.{ ConfigFilename, ConfigNamespace, @@ -13,7 +14,6 @@ import org.enso.projectmanager.boot.Globals.{ SuccessExitCode } import org.enso.projectmanager.boot.configuration.ProjectManagerConfig -import org.enso.projectmanager.util.Logging import org.enso.version.VersionDescription import pureconfig.ConfigSource import pureconfig.generic.auto._ diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/Logging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/Logging.scala deleted file mode 100644 index 2f6efc3b0d6..00000000000 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/Logging.scala +++ /dev/null @@ -1,66 +0,0 @@ -package org.enso.projectmanager.util - -import cats.syntax.either._ -import ch.qos.logback.classic.{Level, LoggerContext} -import org.slf4j.LoggerFactory - -object Logging { - - /** Application log level. */ - sealed trait LogLevel - object LogLevel { - - case object Error extends LogLevel - case object Warning extends LogLevel - case object Info extends LogLevel - case object Debug extends LogLevel - case object Trace extends LogLevel - - /** Convert to logback log level. */ - def toLogback(level: LogLevel): Level = - level match { - case Error => Level.ERROR - case Warning => Level.WARN - case Info => Level.INFO - case Debug => Level.DEBUG - case Trace => Level.TRACE - } - - /** Convert from logback log level. */ - def fromLogback(level: Level): LogLevel = { - level match { - case Level.`ERROR` => Error - case Level.`WARN` => Warning - case Level.`INFO` => Info - case Level.`DEBUG` => Debug - case Level.`TRACE` => Trace - } - } - } - - private val ROOT_LOGGER = "org.enso" - - /** - * Set log level for the application root logger. - * - * @param level the log level - * @return the new log level - */ - def setLogLevel(level: LogLevel): Either[Throwable, LogLevel] = { - Either.catchNonFatal { - val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] - ctx.getLogger(ROOT_LOGGER).setLevel(LogLevel.toLogback(level)) - level - } - } - - /** Get log level of the application root logger. */ - def getLogLevel: Either[Throwable, LogLevel] = { - Either.catchNonFatal { - val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext] - val level = ctx.getLogger(ROOT_LOGGER).getLevel - LogLevel.fromLogback(level) - } - } - -}