From 3291d33d75ddb794d4b36659f51c46ec02fda811 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 27 Jul 2023 10:23:42 +0200 Subject: [PATCH 01/31] Replace a custom logger with sth off the shelf This change disables an Enso's custom logger with an existing, off the shelf logging implementation with some custom setup and appenders. The change attempts to provide a 1:1 replacement for the existing solution. Currently configuration is a mixture of `application.conf` HOCON file with logback's configuration and programatically changed logger's contexts. --- build.sbt | 95 +- .../src/main/resources/application.conf | 29 +- .../resources/language-server.logback.xml | 35 + .../boot/LanguageServerComponent.scala | 16 +- .../enso/languageserver/boot/MainModule.scala | 17 +- .../CompilerBasedDependencyExtractor.scala | 13 +- .../handler/LibraryPublishHandler.scala | 7 +- .../util/UnhandledLogging.scala | 12 +- .../websocket/json/BaseServerTest.scala | 4 +- .../src/main/resources/application.conf | 17 +- .../src/main/resources/launcher-logback.xml | 25 + .../scala/org/enso/launcher/Launcher.scala | 13 +- .../enso/launcher/cli/GlobalCLIOptions.scala | 10 +- .../launcher/cli/LauncherApplication.scala | 15 +- .../enso/launcher/cli/LauncherLogging.scala | 24 +- .../scala/org/enso/launcher/cli/Main.scala | 4 +- .../launcher/components/LauncherRunner.scala | 20 +- .../DistributionUninstaller.scala | 2 +- .../launcher/upgrade/LauncherUpgrader.scala | 6 +- .../components/LauncherRunnerSpec.scala | 62 +- .../org/enso/runner/ContextFactory.scala | 12 +- .../enso/runner/DependencyPreinstaller.scala | 10 +- .../org/enso/runner/LanguageServerApp.scala | 5 +- .../src/main/scala/org/enso/runner/Main.scala | 25 +- .../org/enso/runner/ProjectUploader.scala | 7 +- .../scala/org/enso/runner/RunnerLogging.scala | 37 +- .../org/enso/interpreter/epb/EpbContext.java | 6 +- .../main/java/org/enso/compiler/Cache.java | 4 +- .../interpreter/runtime/ThreadExecutors.java | 2 +- .../enso/compiler/SerializationManager.scala | 29 +- .../interpreter/test/DebuggingEnsoTest.java | 8 +- .../org/enso/jsonrpc/MessageHandler.scala | 2 +- .../repository/LibraryDownloadTest.scala | 20 +- .../org/enso/logger/JavaLoggingForwarder.java | 153 +++ .../org/enso/logger/ApplicationFilter.java | 32 + .../org/enso/logger/LoggerContextSetup.java | 53 + .../java/org/enso/logger/config/Loggers.java | 59 ++ .../enso/logger/config/LoggingService.java | 41 + .../java/org/enso/logger/config/Server.java | 12 + .../JavaLoggingLogHandlerTest.java | 2 +- .../org/enso/logging/ForwardToServer.java | 75 ++ .../java/org/enso/logging/LoggingService.java | 5 + .../main/resources/logging-server.logback.xml | 27 + .../enso/logging/LoggingCollectorHelper.scala | 126 +++ .../enso/logging/LoggingServiceManager.scala | 52 + .../org/enso/logger/akka/AkkaConverter.java | 55 + .../logger/akka/ActorLoggingReceive.scala | 56 + .../logger/akka/ActorMessageLogging.scala | 35 + .../main/java/org/enso/logger/Converter.java | 57 ++ .../scala/org/enso/logger/ColorMode.scala | 20 + .../scala/org/enso/logger/TestAppender.scala | 19 + .../org/enso/logger/TestLogMessage.scala | 17 + .../scala/org/enso/logger/TestLogger.scala | 26 + .../src/main/resources/application.conf | 19 +- .../resources/project-manager.logback.xml | 24 + .../enso/projectmanager/boot/Logging.scala | 18 +- .../enso/projectmanager/boot/MainModule.scala | 5 +- .../projectmanager/boot/ProjectManager.scala | 30 +- .../projectmanager/boot/configuration.scala | 6 +- .../ExecutorWithUnlimitedPool.scala | 3 +- .../LanguageServerDescriptor.scala | 7 +- ...LoggingServiceEndpointRequestHandler.scala | 4 +- .../service/LoggingServiceDescriptor.scala | 5 +- .../util/UnhandledLogging.scala | 11 +- .../enso/projectmanager/BaseServerSpec.scala | 12 +- .../runtimeversionmanager/cli/Arguments.scala | 12 +- .../runtimeversionmanager/runner/Runner.scala | 12 +- tools/simple-library-server/package-lock.json | 960 +----------------- 68 files changed, 1447 insertions(+), 1196 deletions(-) create mode 100644 engine/language-server/src/main/resources/language-server.logback.xml create mode 100644 engine/launcher/src/main/resources/launcher-logback.xml create mode 100644 lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java create mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java create mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java create mode 100644 lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml create mode 100644 lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala create mode 100644 lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala create mode 100644 lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java create mode 100644 lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala create mode 100644 lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala create mode 100644 lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java create mode 100644 lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala create mode 100644 lib/scala/logging-utils/src/test/scala/org/enso/logger/TestAppender.scala create mode 100644 lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogMessage.scala create mode 100644 lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala create mode 100644 lib/scala/project-manager/src/main/resources/project-manager.logback.xml diff --git a/build.sbt b/build.sbt index ac8083cff276..0f2d3b637d42 100644 --- a/build.sbt +++ b/build.sbt @@ -266,6 +266,10 @@ lazy val enso = (project in file(".")) `task-progress-notifications`, `profiling-utils`, `logging-utils`, + `logging-jutil`, + `logging-logback`, + `logging-socket-collector`, + `logging-utils-akka`, filewatcher, `logging-service`, `logging-truffle-connector`, @@ -679,6 +683,70 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test + ) + ) + +lazy val `logging-socket-collector` = project + .in(file("lib/scala/logging-socket-collector")) + .configs(Test) + .settings( + frgaalJavaCompilerSetting, + version := "0.1", + libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, + "ch.qos.logback" % "logback-classic" % logbackClassicVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, + akkaHttp + ) + ) + .dependsOn(`logging-logback`) + +lazy val `logging-jutil` = project + .in(file("lib/scala/logging-jutil")) + .configs(Test) + .settings( + frgaalJavaCompilerSetting, + version := "0.1", + libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, + "ch.qos.logback" % "logback-classic" % logbackClassicVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, + akkaHttp + ) + ).dependsOn(`logging-logback`) + +lazy val `logging-logback` = project + .in(file("lib/scala/logging-logback")) + .configs(Test) + .settings( + frgaalJavaCompilerSetting, + version := "0.1", + libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, + "ch.qos.logback" % "logback-classic" % logbackClassicVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + akkaHttp + ) + ) + + +lazy val `logging-utils-akka` = project + .in(file("lib/scala/logging-utils-akka")) + .configs(Test) + .settings( + frgaalJavaCompilerSetting, + version := "0.1", + libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, + "com.typesafe.akka" %% "akka-actor" % akkaVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ) @@ -880,6 +948,8 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .dependsOn(`polyglot-api`) .dependsOn(`runtime-version-manager`) .dependsOn(`library-manager`) + .dependsOn(`logging-utils-akka`) + .dependsOn(`logging-socket-collector`) .dependsOn(pkg) .dependsOn(`json-rpc-server`) .dependsOn(`json-rpc-server-test` % Test) @@ -1081,6 +1151,7 @@ lazy val `language-server` = (project in file("engine/language-server")) commands += WithDebugCommand.withDebug, frgaalJavaCompilerSetting, libraryDependencies ++= akka ++ circe ++ Seq( + "org.slf4j" % "slf4j-api" % slf4jVersion, "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "io.circe" %% "circe-generic-extras" % circeGenericExtrasVersion, "io.circe" %% "circe-literal" % circeVersion, @@ -1093,7 +1164,7 @@ lazy val `language-server` = (project in file("engine/language-server")) "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided", - "org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion + "org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion, ), Test / testOptions += Tests .Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"), @@ -1138,7 +1209,10 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`library-manager`) .dependsOn(`connected-lock-manager`) .dependsOn(`edition-updater`) - .dependsOn(`logging-service`) + //.dependsOn(`logging-service`) + .dependsOn(`logging-utils-akka`) + .dependsOn(`logging-socket-collector`) + .dependsOn(`logging-jutil`) .dependsOn(`polyglot-api`) .dependsOn(`searcher`) .dependsOn(`text-buffer`) @@ -1675,7 +1749,7 @@ lazy val `engine-runner` = project "org.jline.nativ.JLineLibrary", "io.methvin.watchservice.jna.CarbonAPI", "org.enso.syntax2.Parser", - "org.enso.loggingservice", + //"org.enso.loggingservice", "zio.internal.ZScheduler$$anon$4", "sun.awt", "sun.java2d", @@ -1701,7 +1775,7 @@ lazy val `engine-runner` = project .dependsOn(`library-manager`) .dependsOn(`language-server`) .dependsOn(`polyglot-api`) - .dependsOn(`logging-service`) + //.dependsOn(`logging-service`) lazy val launcher = project .in(file("engine/launcher")) @@ -1767,7 +1841,9 @@ lazy val launcher = project .dependsOn(`runtime-version-manager`) .dependsOn(`version-output`) .dependsOn(pkg) - .dependsOn(`logging-service`) + .dependsOn(`logging-utils` % "test->test") + .dependsOn(`logging-socket-collector`) + //.dependsOn(`logging-service`) .dependsOn(`distribution-manager` % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -1996,7 +2072,7 @@ lazy val `library-manager` = project .dependsOn(`distribution-manager`) .dependsOn(downloader) .dependsOn(testkit % Test) - .dependsOn(`logging-service` % Test) + //.dependsOn(`logging-service` % Test) lazy val `library-manager-test` = project .in(file("lib/scala/library-manager-test")) @@ -2010,8 +2086,9 @@ lazy val `library-manager-test` = project ) ) .dependsOn(`library-manager`) + .dependsOn(`logging-utils` % "test->test") .dependsOn(testkit) - .dependsOn(`logging-service`) + //.dependsOn(`logging-service`) lazy val `connected-lock-manager` = project .in(file("lib/scala/connected-lock-manager")) @@ -2046,7 +2123,7 @@ lazy val `runtime-version-manager` = project ) .dependsOn(pkg) .dependsOn(downloader) - .dependsOn(`logging-service`) + //.dependsOn(`logging-service`) .dependsOn(cli) .dependsOn(`version-output`) .dependsOn(`edition-updater`) @@ -2070,7 +2147,7 @@ lazy val `runtime-version-manager-test` = project .value ) .dependsOn(`runtime-version-manager`) - .dependsOn(`logging-service`) + //.dependsOn(`logging-service`) .dependsOn(testkit) .dependsOn(cli) .dependsOn(`distribution-manager`) diff --git a/engine/language-server/src/main/resources/application.conf b/engine/language-server/src/main/resources/application.conf index 24026ee7d4ba..c8b75e4839e0 100644 --- a/engine/language-server/src/main/resources/application.conf +++ b/engine/language-server/src/main/resources/application.conf @@ -11,13 +11,24 @@ akka { log-dead-letters-during-shutdown = off } -logging-service.logger { - akka.actor = info - akka.event = error - akka.io = error - akka.stream = error - slick.jdbc.JdbcBackend.statement = error # log SQL queries on debug level - slick."*" = error - org.eclipse.jgit = error - io.methvin.watcher = error +logging-service { + logger { + akka.actor = info + akka.event = error + akka.routing = error + akka.io = error + akka.stream = error + slick.jdbc.JdbcBackend.statement = error # log SQL queries on debug level + slick."*" = error + org.eclipse.jgit = error + io.methvin.watcher = error + org.enso.languageserver.protocol.json.JsonConnectionController = debug # very verbose in trace mode + org.enso.jsonrpc.JsonRpcServer = debug # verbose in trace mode + org.enso.languageserver.runtime.RuntimeConnector = debug + } + appender = "forward-via-socket" + server { + hostname = "localhost" + port = 6000 + } } diff --git a/engine/language-server/src/main/resources/language-server.logback.xml b/engine/language-server/src/main/resources/language-server.logback.xml new file mode 100644 index 000000000000..b3b8ce4bfc4d --- /dev/null +++ b/engine/language-server/src/main/resources/language-server.logback.xml @@ -0,0 +1,35 @@ + + + + + + + + + ${logging-server.host} + ${logging-server.port} + 10000 + true + + + + + + + + + diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala index c02b0be2bce3..b25597b655cd 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala @@ -14,8 +14,10 @@ import org.enso.languageserver.runtime.RuntimeKiller.{ RuntimeShutdownResult, ShutDownRuntime } -import org.enso.loggingservice.LogLevel + import org.enso.profiling.{FileSampler, MethodsSampler, NoopSampler} +import org.slf4j.event.Level +import org.enso.logger.LoggerContextSetup import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContextExecutor, Future} @@ -25,7 +27,7 @@ import scala.concurrent.{Await, ExecutionContextExecutor, Future} * @param config a LS config * @param logLevel log level for the Language Server */ -class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel) +class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) extends LifecycleComponent with LazyLogging { @@ -37,10 +39,16 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: LogLevel) /** @inheritdoc */ override def start(): Future[ComponentStarted.type] = { + val logConfig = + this.getClass.getResourceAsStream("/language-server.logback.xml") + if (logConfig != null) { + LoggerContextSetup.setupLogging(logLevel, "language-server", logConfig); + } else { + System.err.println("Unable to set up logging for Language Server.") + } logger.info("Starting Language Server...") val sampler = startSampling(config) - logger.debug("Started [{}].", sampler.getClass.getName) - val module = new MainModule(config, logLevel) + val module = new MainModule(config, logLevel) val bindJsonServer = for { binding <- module.jsonRpcServer.bind(config.interface, config.rpcPort) 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 4e0820451f2a..b6a7b8dc013f 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 @@ -43,19 +43,21 @@ import org.enso.librarymanager.LibraryLocations import org.enso.librarymanager.local.DefaultLocalLibraryProvider import org.enso.librarymanager.published.PublishedLibraryCache import org.enso.lockmanager.server.LockManagerService +import org.enso.logger.Converter import org.enso.logger.masking.{MaskedPath, Masking} -import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel} +import org.enso.logger.JavaLoggingForwarder +import org.enso.logger.akka.AkkaConverter import org.enso.polyglot.{HostAccessFactory, RuntimeOptions, RuntimeServerInfo} import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo} import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator} import org.graalvm.polyglot.Context import org.graalvm.polyglot.io.MessageEndpoint +import org.slf4j.event.Level import org.slf4j.LoggerFactory import java.io.File import java.net.URI import java.time.Clock - import scala.concurrent.duration._ import scala.util.{Failure, Success} @@ -64,7 +66,7 @@ import scala.util.{Failure, Success} * @param serverConfig configuration for the language server * @param logLevel log level for the Language Server */ -class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) { +class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { private val log = LoggerFactory.getLogger(this.getClass) log.info( @@ -294,7 +296,8 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) { .option(RuntimeOptions.PROJECT_ROOT, serverConfig.contentRootPath) .option( RuntimeOptions.LOG_LEVEL, - JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName + Converter.toJavaLevel(logLevel).getName + //JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName ) .option(RuntimeOptions.LOG_MASKING, Masking.isMaskingEnabled.toString) .option(RuntimeOptions.EDITION_OVERRIDE, Info.currentEdition) @@ -306,9 +309,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) { .out(stdOut) .err(stdErr) .in(stdIn) - .logHandler( - JavaLoggingLogHandler.create(JavaLoggingLogHandler.defaultLevelMapping) - ) + .logHandler(new JavaLoggingForwarder(6000)) .serverTransport((uri: URI, peerEndpoint: MessageEndpoint) => { if (uri.toString == RuntimeServerInfo.URI) { val connection = new RuntimeConnector.Endpoint( @@ -322,7 +323,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) { .build() log.trace("Created Runtime context [{}].", context) - system.eventStream.setLogLevel(LogLevel.toAkka(logLevel)) + system.eventStream.setLogLevel(AkkaConverter.toAkka(logLevel)) log.trace("Set akka log level to [{}].", logLevel) val runtimeKiller = diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala index 1724559a5d67..33b69b1ee4c8 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala @@ -2,11 +2,14 @@ package org.enso.languageserver.libraries import org.enso.editions.LibraryName import org.enso.libraryupload.DependencyExtractor -import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel} +import org.enso.logger.Converter + +import org.enso.logger.JavaLoggingForwarder import org.enso.pkg.Package import org.enso.pkg.SourceFile import org.enso.polyglot.{HostAccessFactory, PolyglotContext, RuntimeOptions} import org.graalvm.polyglot.Context +import org.slf4j.event.Level import java.io.File @@ -17,7 +20,7 @@ import java.io.File * @param logLevel the log level to use for the runtime context that will do * the parsing */ -class CompilerBasedDependencyExtractor(logLevel: LogLevel) +class CompilerBasedDependencyExtractor(logLevel: Level) extends DependencyExtractor[File] { /** @inheritdoc */ @@ -60,11 +63,9 @@ class CompilerBasedDependencyExtractor(logLevel: LogLevel) .option("js.foreign-object-prototype", "true") .option( RuntimeOptions.LOG_LEVEL, - JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName - ) - .logHandler( - JavaLoggingLogHandler.create(JavaLoggingLogHandler.defaultLevelMapping) + Converter.toJavaLevel(logLevel).getName ) + .logHandler(new JavaLoggingForwarder(6000)) .build new PolyglotContext(context) } diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala index 265bd0cda400..04c6bae4ee5e 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala @@ -19,7 +19,9 @@ import org.enso.languageserver.libraries.{ import org.enso.languageserver.requesthandler.RequestTimeout import org.enso.languageserver.util.UnhandledLogging import org.enso.libraryupload.{auth, LibraryUploader} -import org.enso.loggingservice.LoggingServiceManager +import org.enso.logger.Converter +//import org.slf4j.LoggerFactory +//import org.enso.loggingservice.LoggingServiceManager import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration @@ -102,7 +104,8 @@ class LibraryPublishHandler( ) val future: Future[UploadSucceeded] = BlockingOperation.run { - val logLevel = LoggingServiceManager.currentLogLevelForThisApplication() + val logLevel = + Converter.defaultLogLevel //LoggingServiceManager.currentLogLevelForThisApplication() val dependencyExtractor = new CompilerBasedDependencyExtractor(logLevel) LibraryUploader(dependencyExtractor) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala index 7b6d69535156..89a9e8cd290e 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala @@ -2,16 +2,20 @@ package org.enso.languageserver.util import akka.actor.Actor import com.typesafe.scalalogging.LazyLogging -import org.enso.loggingservice.LogLevel +import org.enso.logger.akka.AkkaConverter +//import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level trait UnhandledLogging extends LazyLogging { this: Actor => - private val akkaLogLevel = LogLevel + private val akkaLogLevel = AkkaConverter .fromString(context.system.settings.LogLevel) - .getOrElse(LogLevel.Error) + .orElse(Level.ERROR) + //.getOrElse(LogLevel.Error) override def unhandled(message: Any): Unit = { - if (implicitly[Ordering[LogLevel]].lteq(LogLevel.Warning, akkaLogLevel)) { + if (Level.WARN.toInt <= akkaLogLevel.toInt) { + //if (implicitly[Ordering[]].lteq(LogLevel.Warning, akkaLogLevel)) { logger.warn("Received unknown message [{}].", message.getClass) } } diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala index 99e5208d718c..b3852b29a779 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala @@ -45,7 +45,6 @@ import org.enso.languageserver.vcsmanager.{Git, VcsManager} import org.enso.librarymanager.LibraryLocations import org.enso.librarymanager.local.DefaultLocalLibraryProvider import org.enso.librarymanager.published.PublishedLibraryCache -import org.enso.loggingservice.LogLevel import org.enso.pkg.PackageManager import org.enso.polyglot.data.TypeGraph import org.enso.polyglot.runtime.Runtime.Api @@ -57,6 +56,7 @@ import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo} import org.enso.testkit.{EitherValue, WithTemporaryDirectory} import org.enso.text.Sha3_224VersionCalculator import org.scalatest.OptionValues +import org.slf4j.event.Level import java.nio.file.{Files, Path} import java.util.UUID @@ -318,7 +318,7 @@ class BaseServerTest distributionManager, resourceManager, Some(languageHome), - new CompilerBasedDependencyExtractor(logLevel = LogLevel.Warning) + new CompilerBasedDependencyExtractor(logLevel = Level.WARN) ) ) diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index 71ed309c6873..eac6d995eb91 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -4,9 +4,16 @@ akka { stdout-loglevel = "ERROR" } -logging-service.logger { - akka.actor = info - akka.event = error - akka.io = error - akka.stream = error +logging-service { + logger { + akka.actor = info + akka.event = error + akka.io = error + akka.stream = error + } + appender = "forward-via-socket" + server { + hostname = "localhost" + port = 6000 + } } diff --git a/engine/launcher/src/main/resources/launcher-logback.xml b/engine/launcher/src/main/resources/launcher-logback.xml new file mode 100644 index 000000000000..5c5dbc1ef2a0 --- /dev/null +++ b/engine/launcher/src/main/resources/launcher-logback.xml @@ -0,0 +1,25 @@ + + + + + + + $launcher.logRoot}/${launcher.logPrefix}-%d{yyyy-MM-dd}.log + false + true + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + + + ${logging-server.host} + ${logging-server.port} + 10000 + true + + + + + + diff --git a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala index cfd85e5fbd45..27195fab8f35 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala @@ -24,7 +24,8 @@ import org.enso.launcher.installation.{ } import org.enso.launcher.project.ProjectManager import org.enso.launcher.upgrade.LauncherUpgrader -import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level +//import org.enso.loggingservice.LogLevel import org.enso.version.{VersionDescription, VersionDescriptionParameter} /** Implements launcher commands that are run from CLI and can be affected by @@ -207,7 +208,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) { def runRepl( projectPath: Option[Path], versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, useSystemJVM: Boolean, jvmOpts: Seq[(String, String)], additionalArguments: Seq[String] @@ -251,7 +252,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) { def runRun( path: Option[Path], versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, useSystemJVM: Boolean, jvmOpts: Seq[(String, String)], additionalArguments: Seq[String] @@ -293,7 +294,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) { options: LanguageServerOptions, contentRoot: Path, versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, useSystemJVM: Boolean, jvmOpts: Seq[(String, String)], additionalArguments: Seq[String] @@ -331,7 +332,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) { */ def runInstallDependencies( versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, useSystemJVM: Boolean, jvmOpts: Seq[(String, String)], additionalArguments: Seq[String] @@ -396,7 +397,7 @@ case class Launcher(cliOptions: GlobalCLIOptions) { path: Option[Path], uploadUrl: Option[String], authToken: Option[String], - logLevel: LogLevel, + logLevel: Level, useSystemJVM: Boolean, jvmOpts: Seq[(String, String)], additionalArguments: Seq[String] diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala index 9fec4a526df9..0df1077cda56 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala @@ -3,8 +3,12 @@ package org.enso.launcher.cli import akka.http.scaladsl.model.Uri import org.enso.cli.arguments.{Argument, OptsParseError} import org.enso.launcher.cli.GlobalCLIOptions.InternalOptions -import org.enso.loggingservice.ColorMode.{Always, Auto, Never} -import org.enso.loggingservice.{ColorMode, LogLevel} +import org.enso.logger.ColorMode.{Always, Auto, Never} +import org.enso.logger.ColorMode + +//import org.enso.loggingservice.ColorMode.{Always, Auto, Never} +//import org.enso.loggingservice.{ColorMode, LogLevel} +import org.slf4j.event.Level /** Gathers settings set by the global CLI options. * @@ -37,7 +41,7 @@ object GlobalCLIOptions { * processes. */ case class InternalOptions( - launcherLogLevel: Option[LogLevel], + launcherLogLevel: Option[Level], loggerConnectUri: Option[Uri], logMaskingDisabled: Boolean ) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index e6af9dd408db..7f9ba36f8a6b 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -15,9 +15,11 @@ import org.enso.launcher.installation.DistributionInstaller import org.enso.launcher.installation.DistributionInstaller.BundleAction import org.enso.launcher.upgrade.LauncherUpgrader import org.enso.launcher.{cli, Launcher} -import org.enso.loggingservice.{ColorMode, LogLevel} +import org.enso.logger.ColorMode +//import org.enso.loggingservice.{ColorMode, LogLevel} import org.enso.runtimeversionmanager.cli.Arguments._ import org.enso.runtimeversionmanager.runner.LanguageServerOptions +import org.slf4j.event.Level import java.nio.file.Path import java.util.UUID @@ -127,12 +129,12 @@ object LauncherApplication { } private def engineLogLevel = { Opts - .optionalParameter[LogLevel]( + .optionalParameter[Level]( "log-level", "(error | warning | info | debug | trace)", "Sets logging verbosity for the engine. Defaults to info." ) - .withDefault(LogLevel.Info) + .withDefault(Level.INFO) } private def runCommand: Command[Config => Int] = @@ -606,7 +608,7 @@ object LauncherApplication { "running actions. May be needed if program output is piped.", showInUsage = false ) - val logLevel = Opts.optionalParameter[LogLevel]( + val logLevel = Opts.optionalParameter[Level]( GlobalCLIOptions.LOG_LEVEL, "(error | warning | info | debug | trace)", "Sets logging verbosity for the launcher. If not provided, defaults to" + @@ -638,7 +640,6 @@ object LauncherApplication { "Specifies if colors should be used in the output, defaults to auto." ) .withDefault(ColorMode.Auto) - val internalOpts = InternalOpts.topLevelOptions ( @@ -683,13 +684,13 @@ object LauncherApplication { internalOptsCallback(globalCLIOptions) LauncherUpgrader.setCLIOptions(globalCLIOptions) - LauncherLogging.setup( + /*LauncherLogging.setup( logLevel, connectLogger, globalCLIOptions.colorMode, !disableLogMasking, None - ) + )*/ initializeApp() if (version) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 65dfeeaea469..3b799fc19e71 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -1,23 +1,19 @@ package org.enso.launcher.cli import java.nio.file.Path - import org.enso.launcher.distribution.DefaultManagers -import org.enso.loggingservice.{ - ColorMode, - LogLevel, - LoggingServiceManager, - LoggingServiceSetupHelper -} +import org.slf4j.event.Level + +import org.enso.logging.LoggingCollectorHelper import scala.concurrent.ExecutionContext.Implicits.global /** Manages setting up the logging service within the launcher. */ -object LauncherLogging extends LoggingServiceSetupHelper { +object LauncherLogging extends LoggingCollectorHelper { /** @inheritdoc */ - override val defaultLogLevel: LogLevel = LogLevel.Warning + override val defaultLogLevel: Level = Level.WARN /** @inheritdoc */ override val logFileSuffix: String = "enso-launcher" @@ -26,6 +22,8 @@ object LauncherLogging extends LoggingServiceSetupHelper { override lazy val logPath: Path = DefaultManagers.distributionManager.paths.logs + override val logComponentName: String = "launcher" + /** Turns off the main logging service, falling back to just a stderr backend. * * This method should be called as part of uninstalling the distribution. The @@ -35,10 +33,12 @@ object LauncherLogging extends LoggingServiceSetupHelper { * This is necessary on Windows to ensure that the logs file is closed, so * that the log directory can be removed. */ - def prepareForUninstall(colorMode: ColorMode): Unit = { - waitForSetup() + def prepareForUninstall(): Unit = { //colorMode: ColorMode): Unit = { + // TODO + /*waitForSetup() + LoggingServiceManager.replaceWithFallback(printers = Seq(stderrPrinter(colorMode, printExceptions = true)) - ) + )*/ } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index e5b49200dbf7..a5af5af20f46 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -17,7 +17,7 @@ object Main { private def runAppHandlingParseErrors(args: Array[String]): Int = LauncherApplication.application.run(args) match { case Left(errors) => - LauncherLogging.setupFallback() + //TODO: LauncherLogging.setupFallback() CLIOutput.println(errors.mkString("\n")) 1 case Right(exitCode) => @@ -55,7 +55,7 @@ object Main { * @param exitCode exit code to return */ def exit(exitCode: Int): Nothing = { - LauncherLogging.tearDown() + //LauncherLogging.tearDown() DefaultManagers.defaultResourceManager.releaseMainLock() sys.exit(exitCode) } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala index 9e3884700bfe..ea5562a1563a 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala @@ -1,16 +1,18 @@ package org.enso.launcher.components -import akka.http.scaladsl.model.Uri import nl.gn0s1s.bump.SemVer import org.enso.distribution.{DistributionManager, Environment} import org.enso.editions.updater.EditionManager import org.enso.launcher.Constants import org.enso.launcher.project.ProjectManager import org.enso.logger.masking.MaskedPath -import org.enso.loggingservice.LogLevel + +import java.net.URI +//import org.enso.loggingservice.LogLevel import org.enso.runtimeversionmanager.components.RuntimeVersionManager import org.enso.runtimeversionmanager.config.GlobalRunnerConfigurationManager import org.enso.runtimeversionmanager.runner._ +import org.slf4j.event.Level import java.nio.file.{Files, Path} import scala.concurrent.Future @@ -25,7 +27,7 @@ class LauncherRunner( componentsManager: RuntimeVersionManager, editionManager: EditionManager, environment: Environment, - loggerConnection: Future[Option[Uri]] + loggerConnection: Future[Option[URI]] ) extends Runner( componentsManager, distributionManager, @@ -42,7 +44,7 @@ class LauncherRunner( def repl( projectPath: Option[Path], versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = @@ -78,7 +80,7 @@ class LauncherRunner( def run( path: Option[Path], versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = @@ -131,7 +133,7 @@ class LauncherRunner( } private def setLogLevelArgs( - level: LogLevel, + level: Level, logMasking: Boolean ): Seq[String] = Seq("--log-level", level.name) ++ @@ -145,7 +147,7 @@ class LauncherRunner( options: LanguageServerOptions, contentRootPath: Path, versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = @@ -204,7 +206,7 @@ class LauncherRunner( uploadUrl: String, token: Option[String], hideProgress: Boolean, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = @@ -250,7 +252,7 @@ class LauncherRunner( def installDependencies( versionOverride: Option[SemVer], hideProgress: Boolean, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = diff --git a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala index cf50e9877626..d4a2bd4902f2 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala @@ -199,7 +199,7 @@ class DistributionUninstaller( dataRoot.toAbsolutePath.normalize ) if (logsInsideData) { - LauncherLogging.prepareForUninstall(globalCLIOptions.colorMode) + LauncherLogging.prepareForUninstall() //globalCLIOptions.colorMode) } for (dirName <- knownDataDirectories) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala b/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala index 3080e753246d..b21a07a7cf7a 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala @@ -25,8 +25,8 @@ import org.enso.runtimeversionmanager.releases.ReleaseProvider import org.enso.launcher.releases.LauncherRepository import org.enso.launcher.InfoLogger import org.enso.launcher.distribution.DefaultManagers -import org.enso.logger.LoggerSyntax import org.enso.runtimeversionmanager.locking.Resources +import org.slf4j.LoggerFactory import scala.util.Try import scala.util.control.NonFatal @@ -428,7 +428,9 @@ object LauncherUpgrader { upgradeRequiredError: UpgradeRequiredError, originalArguments: Array[String] ): Int = { - val logger = Logger[LauncherUpgrader].enter("auto-upgrade") + val logger = LoggerFactory.getLogger( + classOf[LauncherUpgrader] + ) //.enter("auto-upgrade") val globalCLIOptions = cachedCLIOptions.getOrElse( throw new IllegalStateException( "Upgrade requested but application was not initialized properly." diff --git a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala index 934b4b79c19b..b5078fc18135 100644 --- a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala +++ b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala @@ -10,7 +10,10 @@ import org.enso.runtimeversionmanager.config.GlobalRunnerConfigurationManager import org.enso.runtimeversionmanager.runner._ import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest import org.enso.launcher.project.ProjectManager -import org.enso.loggingservice.{LogLevel, TestLogger} +//import org.enso.loggingservice.{LogLevel, TestLogger} +import org.enso.logger.TestLogger +import org.slf4j.event.Level + import org.enso.testkit.FlakySpec import scala.concurrent.Future @@ -178,23 +181,26 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { val runner = makeFakeRunner() val projectPath = getTestDirectory / "project2" val nightlyVersion = SemVer(0, 0, 0, Some("SNAPSHOT.2000-01-01")) - val (_, logs) = TestLogger.gatherLogs { - runner - .newProject( - path = projectPath, - name = "ProjectName2", - engineVersion = nightlyVersion, - normalizedName = None, - projectTemplate = None, - authorName = None, - authorEmail = None, - additionalArguments = Seq() - ) - .get - } + val (_, logs) = TestLogger.gather[Any, Runner]( + classOf[Runner], { + runner + .newProject( + path = projectPath, + name = "ProjectName2", + engineVersion = nightlyVersion, + normalizedName = None, + projectTemplate = None, + authorName = None, + authorEmail = None, + additionalArguments = Seq() + ) + .get + } + ) + println("ALL LOGS: " + logs.size) assert( logs.exists(msg => - msg.logLevel == LogLevel.Warning && msg.message.contains( + msg.level == Level.WARN && msg.msg.contains( "Consider using a stable version." ) ) @@ -208,7 +214,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { projectPath = None, versionOverride = None, additionalArguments = Seq("arg", "--flag"), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -234,7 +240,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { projectPath = Some(projectPath), versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -249,7 +255,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { projectPath = None, versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -264,7 +270,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { projectPath = Some(projectPath), versionOverride = Some(overridden), additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -293,7 +299,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { contentRootPath = projectPath, versionOverride = None, additionalArguments = Seq("additional"), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -315,7 +321,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { contentRootPath = projectPath, versionOverride = Some(overridden), additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -335,7 +341,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = Some(projectPath), versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -350,7 +356,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = None, versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -365,7 +371,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = Some(projectPath), versionOverride = Some(overridden), additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -380,7 +386,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = None, versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .isFailure, @@ -407,7 +413,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = Some(outsideFile), versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get @@ -432,7 +438,7 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { path = Some(insideFile), versionOverride = None, additionalArguments = Seq(), - logLevel = LogLevel.Info, + logLevel = Level.INFO, logMasking = true ) .get diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 0faff393e031..8c749e64746a 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -1,12 +1,13 @@ package org.enso.runner -import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel} +import org.enso.logger.{Converter, JavaLoggingForwarder} import org.enso.polyglot.debugger.{ DebugServerInfo, DebuggerSessionManagerEndpoint } import org.enso.polyglot.{HostAccessFactory, PolyglotContext, RuntimeOptions} import org.graalvm.polyglot.Context +import org.slf4j.event.Level import java.io.{InputStream, OutputStream} @@ -36,7 +37,7 @@ class ContextFactory { in: InputStream, out: OutputStream, repl: Repl, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, enableIrCaches: Boolean, strictErrors: Boolean = false, @@ -49,6 +50,7 @@ class ContextFactory { executionEnvironment.foreach { name => options.put("enso.ExecutionEnvironment", name) } + val logLevelName = Converter.toJavaLevel(logLevel).getName val context = Context .newBuilder() .allowExperimentalOptions(true) @@ -83,11 +85,9 @@ class ContextFactory { } .option( RuntimeOptions.LOG_LEVEL, - JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName - ) - .logHandler( - JavaLoggingLogHandler.create(JavaLoggingLogHandler.defaultLevelMapping) + logLevelName ) + .logHandler(new JavaLoggingForwarder(6000)) .build new PolyglotContext(context) } diff --git a/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala b/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala index b357d9726032..a3d794747a16 100644 --- a/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala +++ b/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala @@ -1,7 +1,9 @@ package org.enso.runner import cats.implicits.toTraverseOps -import com.typesafe.scalalogging.Logger +import org.slf4j.LoggerFactory +import org.slf4j.event.Level +//import com.typesafe.scalalogging.Logger import org.enso.cli.ProgressBar import org.enso.cli.task.{ProgressReporter, TaskProgress} import org.enso.distribution.locking.{ @@ -16,7 +18,7 @@ import org.enso.editions.{DefaultEdition, EditionResolver} import org.enso.languageserver.libraries.CompilerBasedDependencyExtractor import org.enso.librarymanager.dependencies.DependencyResolver import org.enso.librarymanager.{DefaultLibraryProvider, LibraryResolver} -import org.enso.loggingservice.LogLevel +//import org.enso.loggingservice.LogLevel import org.enso.pkg.PackageManager import java.io.File @@ -28,8 +30,8 @@ object DependencyPreinstaller { * to find all transitive dependencies and ensures that all of them are * installed. */ - def preinstallDependencies(projectRoot: File, logLevel: LogLevel): Unit = { - val logger = Logger[DependencyPreinstaller.type] + def preinstallDependencies(projectRoot: File, logLevel: Level): Unit = { + val logger = LoggerFactory.getLogger(classOf[DependencyPreinstaller.type]) val pkg = PackageManager.Default.loadPackage(projectRoot).get val dependencyExtractor = new CompilerBasedDependencyExtractor(logLevel) diff --git a/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala b/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala index 70101fccc8ae..1fcff089637a 100644 --- a/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala +++ b/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala @@ -4,7 +4,8 @@ import org.enso.languageserver.boot.{ LanguageServerComponent, LanguageServerConfig } -import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level +//import org.enso.loggingservice.LogLevel import java.util.concurrent.Semaphore @@ -26,7 +27,7 @@ object LanguageServerApp { */ def run( config: LanguageServerConfig, - logLevel: LogLevel, + logLevel: Level, deamonize: Boolean ): Unit = { val server = new LanguageServerComponent(config, logLevel) diff --git a/engine/runner/src/main/scala/org/enso/runner/Main.scala b/engine/runner/src/main/scala/org/enso/runner/Main.scala index 4cd1604f9174..a4e071c43971 100644 --- a/engine/runner/src/main/scala/org/enso/runner/Main.scala +++ b/engine/runner/src/main/scala/org/enso/runner/Main.scala @@ -14,7 +14,8 @@ import org.enso.languageserver.boot.{ StartupConfig } import org.enso.libraryupload.LibraryUploader.UploadFailedError -import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level +//import org.enso.loggingservice.LogLevel import org.enso.pkg.{Contact, PackageManager, Template} import org.enso.polyglot.{HostEnsoUtils, LanguageInfo, Module, PolyglotContext} import org.enso.version.VersionDescription @@ -524,7 +525,7 @@ object Main { packagePath: String, shouldCompileDependencies: Boolean, shouldUseGlobalCache: Boolean, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean ): Unit = { val file = new File(packagePath) @@ -575,7 +576,7 @@ object Main { path: String, additionalArgs: Array[String], projectPath: Option[String], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, enableIrCaches: Boolean, enableAutoParallelism: Boolean, @@ -660,7 +661,7 @@ object Main { */ private def genDocs( projectPath: Option[String], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, enableIrCaches: Boolean ): Unit = { @@ -682,7 +683,7 @@ object Main { */ private def generateDocsFrom( path: String, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, enableIrCaches: Boolean ): Unit = { @@ -724,7 +725,7 @@ object Main { */ private def preinstallDependencies( projectPath: Option[String], - logLevel: LogLevel + logLevel: Level ): Unit = projectPath match { case Some(path) => try { @@ -875,7 +876,7 @@ object Main { */ private def runRepl( projectPath: Option[String], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, enableIrCaches: Boolean ): Unit = { @@ -914,7 +915,7 @@ object Main { * @param line a CLI line * @param logLevel log level to set for the engine runtime */ - private def runLanguageServer(line: CommandLine, logLevel: LogLevel): Unit = { + private def runLanguageServer(line: CommandLine, logLevel: Level): Unit = { val maybeConfig = parseServerOptions(line) maybeConfig match { @@ -1000,11 +1001,11 @@ object Main { /** Parses the log level option. */ - def parseLogLevel(levelOption: String): LogLevel = { + def parseLogLevel(levelOption: String): Level = { val name = levelOption.toLowerCase - LogLevel.allLevels.find(_.toString.toLowerCase == name).getOrElse { + Level.values().find(_.name().toLowerCase() == name).getOrElse { val possible = - LogLevel.allLevels.map(_.toString.toLowerCase).mkString(", ") + Level.values().map(_.toString.toLowerCase).mkString(", ") System.err.println(s"Invalid log level. Possible values are $possible.") exitFail() } @@ -1023,7 +1024,7 @@ object Main { /** Default log level to use if the LOG_LEVEL option is not provided. */ - val defaultLogLevel: LogLevel = LogLevel.Error + val defaultLogLevel: Level = Level.ERROR /** Main entry point for the CLI program. * diff --git a/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala b/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala index 9b21fae7fe37..e30693ae67b7 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala @@ -5,8 +5,9 @@ import org.enso.cli.ProgressBar import org.enso.cli.task.{ProgressReporter, TaskProgress} import org.enso.languageserver.libraries.CompilerBasedDependencyExtractor import org.enso.libraryupload.{auth, LibraryUploader} -import org.enso.loggingservice.LogLevel +//import org.enso.loggingservice.LogLevel import org.enso.pkg.PackageManager +import org.slf4j.event.Level import java.nio.file.Path @@ -31,7 +32,7 @@ object ProjectUploader { uploadUrl: String, authToken: Option[String], showProgress: Boolean, - logLevel: LogLevel + logLevel: Level ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global val progressReporter = new ProgressReporter { @@ -69,7 +70,7 @@ object ProjectUploader { * @param logLevel the log level to use for the context gathering * dependencies */ - def updateManifest(projectRoot: Path, logLevel: LogLevel): Unit = { + def updateManifest(projectRoot: Path, logLevel: Level): Unit = { val pkg = PackageManager.Default.loadPackage(projectRoot.toFile).get val dependencyExtractor = new CompilerBasedDependencyExtractor(logLevel) diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index b59221b8435d..1feae6a6eead 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -3,8 +3,11 @@ package org.enso.runner import akka.http.scaladsl.model.Uri import com.typesafe.scalalogging.Logger import org.enso.logger.masking.Masking -import org.enso.loggingservice.printers.StderrPrinter -import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} + +import scala.annotation.unused +//import org.enso.loggingservice.printers.StderrPrinter +import org.slf4j.event.Level +//import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} import scala.concurrent.Future import scala.util.{Failure, Success} @@ -25,18 +28,21 @@ object RunnerLogging { */ def setup( connectionUri: Option[Uri], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) val loggerSetup = connectionUri match { case Some(uri) => - LoggingServiceManager + // TODO + /*LoggingServiceManager .setup( LoggerMode.Client(uri), logLevel - ) + )*/ + Future + .successful(()) .map { _ => logger.trace("Connected to logging service at [{}].", uri) } @@ -58,19 +64,34 @@ object RunnerLogging { } } - private def setupLocalLogger(logLevel: LogLevel): Future[Unit] = + private def setupLocalLogger(@unused logLevel: Level): Future[Unit] = { + // TODO + Future.successful(()) + } + + /*LoggingServiceManager + .setup( + LoggerMode.Local( + Seq(StderrPrinter.create(printExceptions = true)) + ), + logLevel + )*/ + + /*private def setupLocalLogger(logLevel: LogLevel): Future[Unit] = LoggingServiceManager .setup( LoggerMode.Local( Seq(StderrPrinter.create(printExceptions = true)) ), logLevel - ) + )*/ private val logger = Logger[RunnerLogging.type] /** Shuts down the logging service gracefully. */ def tearDown(): Unit = - LoggingServiceManager.tearDown() + // TODO + () + //LoggingServiceManager.tearDown() } diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java index 4e40d4e476b3..a1f4814db76b 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java @@ -63,7 +63,7 @@ private static void initializeLanguages( return; } var log = environment.getLogger(EpbContext.class); - log.log(Level.FINE, "Initializing languages {0}", langs); + log.log(Level.FINE, "Initializing languages {}", langs); var cdl = new CountDownLatch(1); var run = (Consumer) @@ -73,13 +73,13 @@ private static void initializeLanguages( cdl.countDown(); try { for (var l : langs.split(",")) { - log.log(Level.FINEST, "Initializing language {0}", l); + log.log(Level.FINEST, "Initializing language {}", l); long then = System.currentTimeMillis(); var res = context.initializeInternal(null, l); long took = System.currentTimeMillis() - then; log.log( Level.FINE, - "Done initializing language {0} with {1} in {2} ms", + "Done initializing language {} with {} in {} ms", new Object[] {l, res, took}); } } finally { diff --git a/engine/runtime/src/main/java/org/enso/compiler/Cache.java b/engine/runtime/src/main/java/org/enso/compiler/Cache.java index 210f710630c9..dec085d06cb5 100644 --- a/engine/runtime/src/main/java/org/enso/compiler/Cache.java +++ b/engine/runtime/src/main/java/org/enso/compiler/Cache.java @@ -261,7 +261,7 @@ private Optional loadCacheFrom( if (cachedObject != null) { return Optional.of(cachedObject); } else { - logger.log(logLevel, "`{0}` was corrupt on disk.", logName); + logger.log(logLevel, "`{}` was corrupt on disk.", logName); invalidateCache(cacheRoot, logger); return Optional.empty(); } @@ -275,7 +275,7 @@ private Optional loadCacheFrom( return Optional.empty(); } } else { - logger.log(logLevel, "One or more digests did not match for the cache for [{0}].", logName); + logger.log(logLevel, "One or more digests did not match for the cache for [{}].", logName); invalidateCache(cacheRoot, logger); return Optional.empty(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java index 0f7aa3a63cc1..1f43357084e8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java @@ -42,7 +42,7 @@ public void shutdown() { success = false; } if (!success) { - context.getLogger().log(Level.WARNING, "Cannot shutdown {0} thread pool", next.getValue()); + context.getLogger().log(Level.WARNING, "Cannot shutdown {} thread pool", next.getValue()); } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala index 5310c7f22dc3..b2a3054302ee 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala @@ -98,7 +98,7 @@ final class SerializationManager( ): Future[Boolean] = { compiler.context.logSerializationManager( debugLogLevel, - "Requesting serialization for module [{0}].", + "Requesting serialization for module [{}].", module.getName ) val duplicatedIr = compiler.updateMetadata( @@ -142,7 +142,7 @@ final class SerializationManager( ): Future[Boolean] = { compiler.context.logSerializationManager( Level.INFO, - "Requesting serialization for library [{0}].", + "Requesting serialization for library [{}].", libraryName ) @@ -179,7 +179,7 @@ final class SerializationManager( compiler.context.logSerializationManager( debugLogLevel, - "Running serialization for bindings [{0}].", + "Running serialization for bindings [{}].", libraryName ) startSerializing(libraryName.toQualifiedName) @@ -312,14 +312,14 @@ final class SerializationManager( case result @ Some(_: SuggestionsCache.CachedSuggestions) => compiler.context.logSerializationManager( Level.FINE, - "Restored suggestions for library [{0}].", + "Restored suggestions for library [{}].", libraryName ) result case _ => compiler.context.logSerializationManager( Level.FINEST, - "Unable to load suggestions for library [{0}].", + "Unable to load suggestions for library [{}].", libraryName ) None @@ -342,14 +342,14 @@ final class SerializationManager( case result @ Some(_: ImportExportCache.CachedBindings) => compiler.context.logSerializationManager( Level.FINE, - "Restored bindings for library [{0}].", + "Restored bindings for library [{}].", libraryName ) result case _ => compiler.context.logSerializationManager( Level.FINEST, - "Unable to load bindings for library [{0}].", + "Unable to load bindings for library [{}].", libraryName ) None @@ -396,22 +396,23 @@ final class SerializationManager( ) compiler.context.logSerializationManager( debugLogLevel, - "Restored IR from cache for module [{0}] at stage [{1}].", - Array[Object](module.getName, loadedCache.compilationStage()) + "Restored IR from cache for module [{}] at stage [{}].", + module.getName, + loadedCache.compilationStage() ) if (!relinkedIrChecks.contains(false)) { compiler.context.updateModule(module, _.hasCrossModuleLinks(true)) compiler.context.logSerializationManager( debugLogLevel, - "Restored links (early phase) in module [{0}].", + "Restored links (early phase) in module [{}].", module.getName ) Some(true) } else { compiler.context.logSerializationManager( debugLogLevel, - "Could not restore links (early phase) in module [{0}].", + "Could not restore links (early phase) in module [{}].", module.getName ) compiler.context.updateModule(module, _.hasCrossModuleLinks(false)) @@ -420,7 +421,7 @@ final class SerializationManager( case None => compiler.context.logSerializationManager( debugLogLevel, - "Unable to load a cache for module [{0}].", + "Unable to load a cache for module [{}].", module.getName ) None @@ -517,7 +518,7 @@ final class SerializationManager( val jobCount = waitingCount + isSerializing.size compiler.context.logSerializationManager( debugLogLevel, - "Waiting for #{0} serialization jobs to complete.", + "Waiting for #{} serialization jobs to complete.", jobCount ) @@ -584,7 +585,7 @@ final class SerializationManager( compiler.context.logSerializationManager( debugLogLevel, - "Running serialization for module [{0}].", + "Running serialization for module [{}].", name ) startSerializing(name) diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java index 72a848f568ac..b3cf7b917bd9 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java @@ -465,7 +465,7 @@ public void testSteppingOver() { """); List expectedLineNumbers = List.of(3, 4, 5); Queue steps = createStepOverEvents(expectedLineNumbers.size()); - testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); } /** @@ -498,7 +498,7 @@ public void testSteppingOverUseStdLib() { ); List expectedLineNumbers = mapLinesToLineNumbers(src, expectedLines); Queue steps = createStepOverEvents(expectedLineNumbers.size()); - testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); } @Test @@ -515,7 +515,7 @@ public void testSteppingInto() { Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)) ); - testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); } @Test @@ -531,7 +531,7 @@ public void testSteppingIntoMoreExpressionsOneLine() { Queue steps = new ArrayDeque<>( Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)) ); - testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); } private static final class FrameEntry { diff --git a/lib/scala/json-rpc-server/src/main/scala/org/enso/jsonrpc/MessageHandler.scala b/lib/scala/json-rpc-server/src/main/scala/org/enso/jsonrpc/MessageHandler.scala index 196df7a0313d..f54df2371c95 100644 --- a/lib/scala/json-rpc-server/src/main/scala/org/enso/jsonrpc/MessageHandler.scala +++ b/lib/scala/json-rpc-server/src/main/scala/org/enso/jsonrpc/MessageHandler.scala @@ -194,7 +194,7 @@ object MessageHandler { */ case class Connected(webConnection: ActorRef) - /** A control message usef to notify the controller about + /** A control message used to notify the controller about * the connection being closed. */ case object Disconnected diff --git a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala index 86c82bca92d3..0dca502de4ce 100644 --- a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala +++ b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala @@ -1,12 +1,15 @@ package org.enso.librarymanager.published.repository import org.enso.editions.Editions -import org.enso.loggingservice.TestLogger.TestLogMessage -import org.enso.loggingservice.{LogLevel, TestLogger} +import org.enso.librarymanager.published.cache.DownloadingLibraryCache +import org.enso.logger.{TestAppender, TestLogMessage} import org.enso.pkg.PackageManager import org.enso.testkit.WithTemporaryDirectory import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec +import org.slf4j.LoggerFactory +import org.slf4j.event.Level +import ch.qos.logback.classic.Logger import java.nio.file.Files @@ -31,7 +34,13 @@ class LibraryDownloadTest repo.testLib.version ) shouldBe empty - val (libPath, logs) = TestLogger.gatherLogs { + val logger = LoggerFactory + .getLogger(classOf[DownloadingLibraryCache]) + .asInstanceOf[Logger] + val appender = new TestAppender() + logger.addAppender(appender) + //logger.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); + val libPath = cache .findOrInstallLibrary( repo.testLib.libraryName, @@ -40,7 +49,6 @@ class LibraryDownloadTest .Repository("test_repo", s"http://localhost:$port/libraries") ) .get - } val pkg = PackageManager.Default.loadPackage(libPath.location.toFile).get pkg.normalizedName shouldEqual "Bar" @@ -52,9 +60,9 @@ class LibraryDownloadTest "The license file should not exist as it was not provided " + "in the repository." ) - logs should contain( + appender.allEvents() should contain( TestLogMessage( - LogLevel.Warning, + Level.WARN, "License file for library [Foo.Bar:1.0.0] was missing." ) ) diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java new file mode 100644 index 000000000000..07d0fd336f74 --- /dev/null +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java @@ -0,0 +1,153 @@ +package org.enso.logger; + +import static ch.qos.logback.core.net.SocketConnector.ExceptionHandler; +import static java.util.logging.Level.*; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.net.SocketAppender; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.net.DefaultSocketConnector; +import ch.qos.logback.core.net.SocketConnector; +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import org.enso.logger.config.LoggingService; + +/** + * JavaLoggingForwarder will forward all java.util.logging.LogRecord to a Logback's appender. + * Forwarding is dependent upon the presence of the coordinates for the central logging server and + * wheteher it is up and running. + */ +public class JavaLoggingForwarder extends Handler { + private SocketConnector connector; + + private ch.qos.logback.core.Appender appender; + private boolean started; + + public JavaLoggingForwarder(int port) { + this("localhost", port); + } + + public JavaLoggingForwarder(String host, int port) { + try { + InetAddress address = + host.equals("localhost") ? InetAddress.getLocalHost() : InetAddress.getByName(host); + connector = new DefaultSocketConnector(address, port, 0, 10000); + connector.setExceptionHandler(new FallbackExceptionHandler(1)); + } catch (Throwable e) { + connector = null; + } + boolean enabled = canEstablishSocketConnection(); + if (enabled) { + SocketAppender socketAppender = new SocketAppender(); + socketAppender.setPort(port); + socketAppender.setRemoteHost(host); + socketAppender.setIncludeCallerData(false); + LoggerContext ctx = new LoggerContext(); + socketAppender.setContext(new LoggerContext()); + appender = socketAppender; + } else { + appender = new ConsoleAppender(); + } + appender.addFilter(ApplicationFilter.fromLoggers(LoggingService.parseConfig().getLoggers())); + + started = false; + } + + @Override + public void publish(LogRecord record) { + if (!started) { + synchronized (this) { + if (!started) { + started = true; + appender.start(); + } + } + } + appender.doAppend(toEvent(record)); + } + + private boolean canEstablishSocketConnection() { + if (connector == null) return false; + try { + Socket socket = connector.call(); + boolean test = socket != null; + if (test) { + socket.close(); + } + return test; + } catch (InterruptedException | IOException e) { + System.err.println("Failed to establish connection: " + e.getCause().getMessage()); + return false; + } catch (RuntimeException e) { + if (!(e.getCause() instanceof ConnectException)) { + System.err.println("Failed to establish connection: " + e.getCause().getMessage()); + } + return false; + } + } + + @Override + public void flush() {} + + @Override + public void close() throws SecurityException { + // flush all remaining events + if (appender != null) appender.stop(); + } + + private ILoggingEvent toEvent(LogRecord record) { + LoggingEvent event = new LoggingEvent(); + event.setLoggerName(record.getLoggerName()); + event.setLevel(toLogbackLevel(record.getLevel())); + event.setMessage(record.getMessage()); + event.setArgumentArray(record.getParameters()); + event.setInstant(record.getInstant()); + if (record.getThrown() != null) { + event.setThrowableProxy(new ThrowableProxy(record.getThrown())); + } + event.prepareForDeferredProcessing(); + return event; + } + + private static ch.qos.logback.classic.Level toLogbackLevel(java.util.logging.Level julLevel) { + if (julLevel.intValue() == SEVERE.intValue()) return ch.qos.logback.classic.Level.ERROR; + else if (julLevel.intValue() == INFO.intValue()) return ch.qos.logback.classic.Level.INFO; + else if (julLevel.intValue() == WARNING.intValue()) return ch.qos.logback.classic.Level.WARN; + else if (julLevel.intValue() == ALL.intValue()) return ch.qos.logback.classic.Level.ALL; + else if (julLevel.intValue() == FINE.intValue()) return ch.qos.logback.classic.Level.DEBUG; + else if (julLevel.intValue() == FINER.intValue()) return ch.qos.logback.classic.Level.TRACE; + else if (julLevel.intValue() == FINEST.intValue()) return ch.qos.logback.classic.Level.TRACE; + else return ch.qos.logback.classic.Level.OFF; + } + + /** + * Local exception handler that propagates a failure to establish the connection. That way, + * establishing a socket connection is not attempted infinitely on failure. + */ + private class FallbackExceptionHandler implements ExceptionHandler { + int retries; + + public FallbackExceptionHandler(int retries) { + this.retries = retries; + } + + public FallbackExceptionHandler() { + this(2); + } + + public void connectionFailed(SocketConnector connector, Exception ex) { + if (retries == 0) { + throw new RuntimeException(ex); + } else { + retries = retries - 1; + } + } + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java new file mode 100644 index 000000000000..296b0f17cc79 --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java @@ -0,0 +1,32 @@ +package org.enso.logger; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.enso.logger.config.Loggers; + +public class ApplicationFilter extends Filter { + private final Loggers loggers; + + private ApplicationFilter(Loggers loggers) { + this.loggers = loggers; + } + + @Override + public FilterReply decide(ILoggingEvent event) { + for (var entry : loggers.entrySet()) { + if (event.getLoggerName().startsWith(entry.getKey())) { + if (event.getLevel().isGreaterOrEqual(entry.getValue())) { + return FilterReply.NEUTRAL; + } else { + return FilterReply.DENY; + } + } + } + return FilterReply.NEUTRAL; + } + + public static Filter fromLoggers(Loggers loggers) { + return new ApplicationFilter(loggers); + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java new file mode 100644 index 000000000000..8747e372ad94 --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java @@ -0,0 +1,53 @@ +package org.enso.logger; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.util.StatusPrinter; +import java.io.InputStream; +import org.enso.logger.config.LoggingService; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +public class LoggerContextSetup { + + public static Boolean setupLogging( + Level level, String componentName, InputStream customLogConfig) { + return setupLogging(level, componentName, customLogConfig, LoggingService.parseConfig()); + } + + public static Boolean setupLogging( + Level logLevel, String componentName, InputStream customLogConfig, LoggingService appConfig) { + var context = (LoggerContext) LoggerFactory.getILoggerFactory(); + var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); + System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); + System.setProperty(componentName + ".appender", appConfig.getAppender()); + var serverConfig = appConfig.getServer(); + System.setProperty("logging-server.host", serverConfig.hostname()); + System.setProperty("logging-server.port", String.valueOf(serverConfig.port())); + try { + var configurator = new JoranConfigurator(); + configurator.setContext(context); + // Call context.reset() to clear any previous configuration, e.g. default + // configuration. + context.reset(); + configurator.doConfigure(customLogConfig); + var rootLogger = context.getLogger("root"); + var appender = rootLogger.getAppender(appConfig.getAppender()); + if (appender == null) { + System.err.println( + "Failed to apply custom log levels for application loggers' in " + + appConfig.getAppender()); + } else { + var filter = ApplicationFilter.fromLoggers(appConfig.getLoggers()); + appender.addFilter(filter); + } + return true; + } catch (JoranException je) { + je.printStackTrace(); + StatusPrinter.printInCaseOfErrorsOrWarnings(context); + System.err.println("Failed to setup Logback configuration"); + return false; + } + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java new file mode 100644 index 000000000000..414be05d96aa --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java @@ -0,0 +1,59 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class Loggers { + + private Map loggers; + + private Loggers(Map loggers) { + this.loggers = loggers; + } + + public Set> entrySet() { + return loggers.entrySet(); + } + + public static Loggers parse(Config config) { + // LinkedHashMap ensures that wildcard loggers are de-prioritized + Map loggers = new LinkedHashMap<>(); + Map fallbacks = new LinkedHashMap<>(); + config + .entrySet() + .forEach( + entry -> { + String key = entry.getKey(); + String v = config.getString(key); + ch.qos.logback.classic.Level level = ch.qos.logback.classic.Level.toLevel(v); + String normalizedKey = normalizeKey(key); + + if (normalizedKey.endsWith("*")) { + int idx = normalizedKey.indexOf('*'); + fallbacks.put(normalizedKey.substring(0, idx), level); + } else { + loggers.put(normalizedKey, level); + } + }); + if (!fallbacks.isEmpty()) { + loggers.putAll(fallbacks); + } + return new Loggers(loggers); + } + + @Override + public java.lang.String toString() { + return String.join( + "\n", + loggers.entrySet().stream() + .map(entry -> entry.getKey() + ": " + entry.getValue().toString()) + .collect(Collectors.toList())); + } + + private static String normalizeKey(String key) { + return key.replace("'", "").replace("\"", ""); + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java new file mode 100644 index 000000000000..cc61962799ee --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java @@ -0,0 +1,41 @@ +package org.enso.logger.config; + +import com.typesafe.config.ConfigFactory; + +public class LoggingService { + public static final String configurationRoot = "logging-service"; + public static final String serverKey = "server"; + public static final String loggersKey = "logger"; + + public static final String appenderKey = "appender"; + + private Loggers loggers; + private String appender; + private Server server; + + private LoggingService(Loggers loggers, String appender, Server server) { + this.loggers = loggers; + this.appender = appender; + this.server = server; + } + + public static LoggingService parseConfig() { + var empty = ConfigFactory.empty().atKey(configurationRoot); + var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); + Server server = Server.parse(root.getConfig(serverKey)); + Loggers loggers = Loggers.parse(root.getConfig(loggersKey)); + return new LoggingService(loggers, root.getString(appenderKey), server); + } + + public Loggers getLoggers() { + return loggers; + } + + public String getAppender() { + return appender; + } + + public Server getServer() { + return server; + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java new file mode 100644 index 000000000000..84193efb93b9 --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java @@ -0,0 +1,12 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public record Server(String hostname, int port) { + + public static Server parse(Config config) { + int port = config.getInt("port"); + String hostname = config.getString("hostname"); + return new Server(hostname, port); + } +} diff --git a/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java b/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java index 6051a95d95b7..dfcba4e44737 100644 --- a/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java +++ b/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java @@ -41,7 +41,7 @@ public boolean isEnabled(String name, LogLevel level) { }; var h = new JavaLoggingLogHandler((v1) -> LogLevel.Debug$.MODULE$, c); - LogRecord r = new LogRecord(Level.SEVERE, "Init {0} done"); + LogRecord r = new LogRecord(Level.SEVERE, "Init {} done"); r.setParameters(new Object[] {"js"}); h.publish(r); diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java new file mode 100644 index 000000000000..f84a5101c8d9 --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java @@ -0,0 +1,75 @@ +package org.enso.logging; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.net.SimpleSocketServer; +import ch.qos.logback.core.joran.spi.JoranException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import org.slf4j.event.Level; + +class ForwardToServer extends LoggingService { + private static final String rollingFileAppender = "rolling-file"; + private static final String consoleAppender = "console"; + private static final String socketForwarderAppender = "socket-forwarder"; + + private int port; + private SimpleSocketServer logServer; + + public ForwardToServer(int port) { + this.port = port; + this.logServer = null; + } + + public URI logToFile(Level level, Path path, String prefix) + throws URISyntaxException, JoranException { + var lc = new LoggerContext(); + var configurator = new JoranConfigurator(); + System.setProperty("logging-server.logRoot", path.toAbsolutePath().toString()); + System.setProperty("logging-server.logPrefix", prefix); + System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); + System.setProperty("logging-server.appender", rollingFileAppender); + configurator.setContext(lc); + configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); + logServer = new SimpleSocketServer(lc, port); + logServer.start(); + return new URI(null, null, "localhost", port, null, null, null); + } + + public void forwardToSocket(Level level, String targetHostname, int targetPort) + throws JoranException { + var lc = new LoggerContext(); + var configurator = new JoranConfigurator(); + configurator.setContext(lc); + System.setProperty("logging-server.targetHost", targetHostname); + System.setProperty("logging-server.targetPort", String.valueOf(targetPort)); + System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); + System.setProperty("logging-server.appender", socketForwarderAppender); + configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); + logServer = new SimpleSocketServer(lc, port); + logServer.start(); + } + + public void logToConsole(Level level) throws JoranException { + var lc = new LoggerContext(); + var configurator = new JoranConfigurator(); + configurator.setContext(lc); + System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); + System.setProperty("logging-server.appender", consoleAppender); + configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); + logServer = new SimpleSocketServer(lc, port); + logServer.start(); + } + + public boolean isSetup() { + return logServer != null; + } + + @Override + public void teardown() { + if (logServer != null) { + logServer.close(); + } + } +} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java new file mode 100644 index 000000000000..b9341782e9b7 --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java @@ -0,0 +1,5 @@ +package org.enso.logging; + +public abstract class LoggingService { + public abstract void teardown(); +} diff --git a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml new file mode 100644 index 000000000000..e1c3b8db6a89 --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml @@ -0,0 +1,27 @@ + + + + + ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.log + ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + 2GB + + false + true + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + + + + + + diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala new file mode 100644 index 000000000000..7b8c555ebe9c --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala @@ -0,0 +1,126 @@ +package org.enso.logging + +import org.slf4j.event.Level + +import java.io.InputStream +import java.net.URI +import java.nio.file.Path +import scala.annotation.unused +import scala.concurrent.Future +import scala.concurrent.ExecutionContext +import scala.concurrent.Promise + +import scala.util.{Failure, Success} +import org.enso.logger.LoggerContextSetup +import org.enso.logger.config.LoggingService + +abstract class LoggingCollectorHelper(implicit + executionContext: ExecutionContext +) { + val defaultLogLevel: Level + + val logFileSuffix: String + + def logPath: Path + + def logComponentName: String + + def logConfiguration(): Option[InputStream] = { + val resource = + this.getClass.getResourceAsStream(s"/${logComponentName}.logback.xml") + Option.when(resource != null)(resource) + } + + protected def logLevelPropertyName: String = s"${logComponentName}.logLevel" + + protected def appenderPropertyName: String = s"${logComponentName}.appender" + + def loggingServiceEndpoint(): Future[Option[URI]] = Future.successful(None) + + private val loggingServiceEndpointPromise = Promise[Option[URI]]() + + def setup( + logLevel: Option[Level], + connectToExternalLogger: Option[URI], + @unused logMasking: Boolean, + @unused profilingLog: Option[Path] + ): Unit = { + val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + val loggingService = LoggingService.parseConfig() + //TODO: Masking.setup(logMasking) + val (host, port, serverNeedsInitialization) = + connectToExternalLogger match { + case Some(uri) => + (uri.getHost(), uri.getPort(), false) + case None => + ( + loggingService.getServer().hostname(), + loggingService.getServer().port(), + true + ) + } + + if (serverNeedsInitialization) { + LoggingServiceManager + .setupServer(actualLogLevel, port, logPath, logFileSuffix) + .onComplete { + case Failure(exception) => + exception.printStackTrace() + System.setProperty( + s"${logComponentName}.logRoot", + logPath.toAbsolutePath.toString + ) + System.setProperty( + s"${logComponentName}.logPrefix", + logFileSuffix + "-standalone" + ) + System.setProperty( + logLevelPropertyName, + actualLogLevel.toString.toLowerCase() + ) + // fallback to whatever is the default appender + case Success(uri) => + val appender = loggingService.getAppender() + System.setProperty(appenderPropertyName, appender) + System.setProperty("logging-server.host", host) + System.setProperty("logging-server.port", port.toString) + System.setProperty( + logLevelPropertyName, + actualLogLevel.toString.toLowerCase() + ) + System.setProperty(appenderPropertyName, appender) + val result = logConfiguration() + .map(conf => + Boolean.unbox( + LoggerContextSetup + .setupLogging(actualLogLevel, logComponentName, conf) + ) + ) + .getOrElse(false) + if (!result) { + System.err.println("Failed to set Logger Context") + } + loggingServiceEndpointPromise.success(Some(uri)) + } + } else { + System.setProperty("logging-server.host", host) + System.setProperty("logging-server.port", port.toString) + System.setProperty( + logLevelPropertyName, + actualLogLevel.toString.toLowerCase() + ) + System.setProperty(appenderPropertyName, "forward-via-socket") + val result = logConfiguration() + .map(conf => + Boolean.unbox( + LoggerContextSetup + .setupLogging(actualLogLevel, logComponentName, conf) + ) + ) + .getOrElse(false) + if (!result) { + System.err.println("Failed to set Logger Context") + } + } + } +} diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala new file mode 100644 index 000000000000..8e1ea623224e --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -0,0 +1,52 @@ +package org.enso.logging + +import java.net.URI +import scala.util.{Success, Try} +import org.slf4j.event.Level +import java.nio.file.Path +import scala.concurrent.Future +import scala.concurrent.ExecutionContext + +object LoggingServiceManager { + + private[this] var loggingService: LoggingService = null + private[this] var currentLevel: Level = Level.TRACE + + def currentLogLevelForThisApplication(): Level = currentLevel + + def setupServer( + logLevel: Level, + port: Int, + logPath: Path, + logFileSuffix: String + )(implicit ec: ExecutionContext): Future[URI] = { + if (loggingService != null) { + throw new RuntimeException("logging service already setup") + } else { + currentLevel = logLevel + val forwarder = new ForwardToServer(port) + loggingService = forwarder + Future { + forwarder.logToFile(logLevel, logPath, logFileSuffix) + } + } + } + + def fallbackToLocalConsole(logLevel: Level): Try[Unit] = { + if (loggingService != null) { + loggingService.teardown() + } + System.setProperty("logging-server.logLevel", logLevel.toString.toLowerCase) + System.setProperty("logging-server.appender", "console") + + // TODO: start console appender + Success(()) + } + + def teardown(): Unit = { + if (loggingService != null) { + loggingService.teardown() + } + } + +} diff --git a/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java b/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java new file mode 100644 index 000000000000..36ca444694a7 --- /dev/null +++ b/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java @@ -0,0 +1,55 @@ +package org.enso.logger.akka; + +import static org.slf4j.event.Level.*; + +import java.util.Optional; +import org.slf4j.event.Level; + +public class AkkaConverter { + + /** + * Converts SLF4K's Level to an Akka one. + * + * @param level a SLF4J's Level to convert + * @return an equivalnet of `level` in Akka's LogLevel + */ + public static akka.event.Logging.LogLevel toAkka(Level level) { + switch (level) { + case ERROR: + return new akka.event.Logging.LogLevel(1); // Error + case WARN: + return new akka.event.Logging.LogLevel(2); // Warning + case INFO: + return new akka.event.Logging.LogLevel(3); // Info + case DEBUG: + return new akka.event.Logging.LogLevel(4); // Debug + case TRACE: + return new akka.event.Logging.LogLevel(4); // Debug + default: + return new akka.event.Logging.LogLevel(1); + } + } + + /** + * Converts a string representation of Akka's LogLevel to an SLF4J one. + * + * @param level a string representation of level to convert + * @return an equivalent representation in Akka's LogLevel + */ + public static Optional fromString(String level) { + switch (level.toLowerCase()) { + case "warning": + return Optional.of(WARN); + case "error": + return Optional.of(ERROR); + case "info": + return Optional.of(INFO); + case "debug": + return Optional.of(DEBUG); + case "trace": + return Optional.of(TRACE); + default: + return Optional.empty(); + } + } +} diff --git a/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala b/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala new file mode 100644 index 000000000000..3382e6796398 --- /dev/null +++ b/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala @@ -0,0 +1,56 @@ +package org.enso.logger.akka + +import akka.actor.Actor.Receive +import akka.actor.ActorContext +import org.slf4j.Logger + +/** The decorator adding invocation logging to a Receive function of an actor. + * + * @param label the label `" in state " + label` appended to each message + * @param logger the logger that will be used for logging actor messages + * @param r the receive function of an actor + * @param context the actor context + */ +class ActorLoggingReceive( + label: Option[String], + logger: Logger, + r: Receive +)(implicit context: ActorContext) + extends Receive { + + /** @inheritdoc */ + override def isDefinedAt(o: Any): Boolean = { + val handled = r.isDefinedAt(o) + val labelText = label match { + case Some(l) => " in state " + l + case _ => "" + } + val template = + s"received ${if (handled) "handled" else "unhandled"} {} from {}$labelText" + logger.trace(template, o, context.sender()) + handled + } + + /** @inheritdoc */ + override def apply(o: Any): Unit = r(o) +} + +object ActorLoggingReceive { + + /** Create the [[ActorLoggingReceive]] decorator adding invocation logging + * to a Receive function. + * + * @param label the label `" in state " + label` appended to each message + * @param logger the logger + * @param r the original receive function + */ + def apply(label: Option[String], logger: Logger, r: Receive)(implicit + context: ActorContext + ): Receive = r match { + case _: ActorLoggingReceive => + r + case _ => + new ActorLoggingReceive(label, logger, r) + + } +} diff --git a/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala b/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala new file mode 100644 index 000000000000..a54e1332788e --- /dev/null +++ b/lib/scala/logging-utils-akka/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala @@ -0,0 +1,35 @@ +package org.enso.logger.akka + +import akka.actor.Actor +import akka.actor.ActorContext +import org.slf4j.LoggerFactory + +/** A trait providing functions for logging received actor messages. */ +trait ActorMessageLogging { outer: Actor => + + object LoggingReceive { + + private val logger = LoggerFactory.getLogger(outer.getClass) + + /** Wrap a Receive partial function in a logging enclosure, which logs a + * message each time before a message is matched. This includes messages + * which are not handled. + * + * {{{ + * def receive = LoggingReceive { + * case x => ... + * } + * }}} + */ + def apply(r: Receive)(implicit context: ActorContext): Receive = + ActorLoggingReceive(None, logger, r) + + /** Create a decorated logger which appends `" in state " + label` + * to each message it logs. + */ + def withLabel(label: String)(r: Receive)(implicit + context: ActorContext + ): Receive = + ActorLoggingReceive(Some(label), logger, r) + } +} diff --git a/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java b/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java new file mode 100644 index 000000000000..f9fc31f36024 --- /dev/null +++ b/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java @@ -0,0 +1,57 @@ +package org.enso.logger; + +import static org.slf4j.event.Level.*; + +import org.slf4j.event.Level; + +public class Converter { + + public static Level defaultLogLevel = TRACE; + + /** Determines what is the smallest Java level that is still debug and not trace. */ + private static int defaultLevelDebugCutOff = + Math.min(java.util.logging.Level.FINE.intValue(), java.util.logging.Level.CONFIG.intValue()); + + public static String backwardCompatibleName(String name) { + switch (name) { + case "warning": + return "warn"; + default: + return name; + } + } + + /** + * Converts SLF4J's Level to java.util one. + * + * @param level the SLF4J's level to convert. + * @return an equivalent in java.util.logging.Level terms + */ + public static java.util.logging.Level toJavaLevel(Level level) { + switch (level) { + case ERROR: + return java.util.logging.Level.SEVERE; + case WARN: + return java.util.logging.Level.WARNING; + case INFO: + return java.util.logging.Level.INFO; + case DEBUG: + return java.util.logging.Level.FINE; + case TRACE: + return java.util.logging.Level.FINEST; + default: + return java.util.logging.Level.ALL; + } + } + + /** Default mapping of Java log levels to our log levels based */ + public static Level fromJavaLevel(java.util.logging.Level javaLevel) { + int level = javaLevel.intValue(); + if (level == java.util.logging.Level.OFF.intValue()) return ERROR; + else if (level >= java.util.logging.Level.SEVERE.intValue()) return ERROR; + else if (level >= java.util.logging.Level.WARNING.intValue()) return WARN; + else if (level >= java.util.logging.Level.INFO.intValue()) return INFO; + else if (level >= defaultLevelDebugCutOff) return DEBUG; + else return TRACE; + } +} diff --git a/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala b/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala new file mode 100644 index 000000000000..ed24e2aacea0 --- /dev/null +++ b/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala @@ -0,0 +1,20 @@ +package org.enso.logger + +/** Describes possible modes of color display in console output. */ +sealed trait ColorMode +object ColorMode { + + /** Never use color escape sequences in the output. */ + case object Never extends ColorMode + + /** Enable color output if it seems to be supported. */ + case object Auto extends ColorMode + + /** Always use escape sequences in the output, even if the program thinks they + * are unsupported. + * + * May be useful if output is piped to other programs that know how to handle + * the escape sequences. + */ + case object Always extends ColorMode +} diff --git a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestAppender.scala b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestAppender.scala new file mode 100644 index 000000000000..b41fa3336be1 --- /dev/null +++ b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestAppender.scala @@ -0,0 +1,19 @@ +package org.enso.logger + +import ch.qos.logback.core.read.ListAppender +import ch.qos.logback.classic.spi.ILoggingEvent + +import scala.jdk.CollectionConverters._ + +class TestAppender extends ListAppender[ILoggingEvent] { + + def size(): Int = { + this.list.size(); + } + + def allEvents(): List[TestLogMessage] = { + this.list.asScala.toList.map(event => + TestLogMessage(event.getLevel(), event.getFormattedMessage()) + ) + } +} diff --git a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogMessage.scala b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogMessage.scala new file mode 100644 index 000000000000..5c098e1add21 --- /dev/null +++ b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogMessage.scala @@ -0,0 +1,17 @@ +package org.enso.logger + +import org.slf4j.event.Level +import ch.qos.logback.classic.{Level => LogbackLevel} + +case class TestLogMessage(level: Level, msg: String) +object TestLogMessage { + def apply(level: LogbackLevel, msg: String): TestLogMessage = { + val translatedLevel = level match { + case LogbackLevel.INFO => Level.INFO + case LogbackLevel.DEBUG => Level.DEBUG + case LogbackLevel.WARN => Level.WARN + case LogbackLevel.TRACE => Level.TRACE + } + TestLogMessage(translatedLevel, msg) + } +} diff --git a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala new file mode 100644 index 000000000000..306d1b21b903 --- /dev/null +++ b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala @@ -0,0 +1,26 @@ +package org.enso.logger + +import org.slf4j.LoggerFactory +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.LoggerContext + +object TestLogger { + + def gather[T, A](of: Class[A], action: => T): (T, List[TestLogMessage]) = { + val logger = LoggerFactory.getLogger(of).asInstanceOf[Logger] + //val (_, logs) = TestLogger.gatherLogs { + val appender = new TestAppender() + appender.setContext( + LoggerFactory.getILoggerFactory().asInstanceOf[LoggerContext] + ); + + logger.addAppender(appender) + appender.start() + println( + "Found logs; " + appender.size() + " appender: " + appender.isStarted() + ) + val result = action + (result, appender.allEvents()) + } + +} diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index dff5a54f21c1..0eea1c84b0b5 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -11,11 +11,20 @@ akka { log-dead-letters-during-shutdown = off } -logging-service.logger { - akka.actor = info - akka.event = error - akka.io = error - akka.stream = error +logging-service { + logger { + akka.actor = info + akka.event = error + akka.io = error + akka.stream = error + akka.routing = error + } + appender = "forward-via-socket" + server { + hostname = "localhost" + port = 6000 + mode = "file" # file/console/socket/sentry + } } project-manager { diff --git a/lib/scala/project-manager/src/main/resources/project-manager.logback.xml b/lib/scala/project-manager/src/main/resources/project-manager.logback.xml new file mode 100644 index 000000000000..dc31b6d8512d --- /dev/null +++ b/lib/scala/project-manager/src/main/resources/project-manager.logback.xml @@ -0,0 +1,24 @@ + + + + + + ${project-manager.logRoot}/${project-manager.logPrefix}-%d{yyyy-MM-dd}.log + false + true + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + + + ${logging-server.host} + ${logging-server.port} + 10000 + true + + + + + + diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala index c0b89fa59aa5..f64f29d13a8e 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala @@ -1,20 +1,21 @@ package org.enso.projectmanager.boot import java.nio.file.Path - -import akka.http.scaladsl.model.Uri -import org.enso.loggingservice.{LogLevel, LoggingServiceSetupHelper} import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.versionmanagement.DefaultDistributionConfiguration +import org.slf4j.event.Level + +import java.net.URI -import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future +import org.enso.logging.LoggingCollectorHelper +import scala.concurrent.ExecutionContext.Implicits.global /** A helper for setting up the logging service in the Project Manager. */ -object Logging extends LoggingServiceSetupHelper { +object Logging extends LoggingCollectorHelper { /** @inheritdoc */ - override val defaultLogLevel: LogLevel = LogLevel.Info + override val defaultLogLevel: Level = Level.INFO /** @inheritdoc */ override lazy val logPath: Path = @@ -23,9 +24,12 @@ object Logging extends LoggingServiceSetupHelper { /** @inheritdoc */ override val logFileSuffix: String = "enso-project-manager" + override def logComponentName: String = "project-manager" + //override def defaultAppenderName: Option[String] = Some("forward-via-socket") + object GlobalLoggingService extends LoggingServiceDescriptor { /** @inheritdoc */ - override def getEndpoint: Future[Option[Uri]] = loggingServiceEndpoint() + override def getEndpoint: Future[Option[URI]] = loggingServiceEndpoint() } } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala index 369aa03fa33c..34e0ae35c02a 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala @@ -4,7 +4,8 @@ import akka.actor.ActorSystem import akka.stream.SystemMaterializer import cats.MonadError import org.enso.jsonrpc.JsonRpcServer -import org.enso.loggingservice.LogLevel +import org.enso.logger.akka.AkkaConverter +//import org.enso.loggingservice.LogLevel import org.enso.projectmanager.boot.configuration.{ MainProcessConfig, ProjectManagerConfig @@ -53,7 +54,7 @@ class MainModule[ implicit val system: ActorSystem = ActorSystem("project-manager", None, None, Some(computeExecutionContext)) - system.eventStream.setLogLevel(LogLevel.toAkka(processConfig.logLevel)) + system.eventStream.setLogLevel(AkkaConverter.toAkka(processConfig.logLevel)) implicit val materializer: SystemMaterializer = SystemMaterializer.get(system) 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 a41ff8230e66..affce7ac479f 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 @@ -3,7 +3,9 @@ package org.enso.projectmanager.boot import akka.http.scaladsl.Http import com.typesafe.scalalogging.LazyLogging import org.apache.commons.cli.CommandLine -import org.enso.loggingservice.{ColorMode, LogLevel} + +import scala.annotation.unused +//import org.enso.loggingservice.{ColorMode, LogLevel} import org.enso.projectmanager.boot.Globals.{ ConfigFilename, ConfigNamespace, @@ -15,6 +17,7 @@ import org.enso.projectmanager.boot.configuration.{ ProjectManagerConfig } import org.enso.version.VersionDescription +import org.slf4j.event.Level import pureconfig.ConfigSource import pureconfig.generic.auto._ import zio.Console.{printLine, printLineError, readLine} @@ -126,7 +129,9 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { case Right(opts) => runOpts(opts).catchAll(th => ZIO.succeed( - logger.error("An error occurred during the program startup", th) + System.err.println( + s"An error occurred during the program startup: ${th.getMessage}" + ) ) *> ZIO.succeed(FailureExitCode) ) @@ -134,7 +139,9 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { (printLine(error) *> ZIO.succeed(Cli.printHelp()) *> ZIO.succeed(FailureExitCode)).catchAll(th => - ZIO.succeed(logger.error("Unexpected error", th)) *> + ZIO.succeed( + System.err.println(s"Unexpected error: ${th.getMessage}") + ) *> ZIO.succeed(FailureExitCode) ) } @@ -256,13 +263,13 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { private def setupLogging( verbosityLevel: Int, - logMasking: Boolean, - profilingLog: Option[Path] - ): ZIO[ZAny, IOException, LogLevel] = { + @unused logMasking: Boolean, + @unused profilingLog: Option[Path] + ): ZIO[ZAny, IOException, Level] = { val level = verbosityLevel match { - case 0 => LogLevel.Info - case 1 => LogLevel.Debug - case _ => LogLevel.Trace + case 0 => Level.INFO + case 1 => Level.DEBUG + case _ => Level.TRACE } // TODO [RW] at some point we may want to allow customization of color @@ -271,10 +278,11 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ZIO .attempt { - Logging.setup(Some(level), None, colorMode, logMasking, profilingLog) + Logging.setup(Some(level), None, logMasking, profilingLog) + () } .catchAll { exception => - printLineError(s"Failed to setup the logger: $exception") + printLineError(s"Failed to setup logger: ${exception.getMessage()}") } .as(level) } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala index 5cfdeefeec51..a06344505ad3 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala @@ -1,10 +1,10 @@ package org.enso.projectmanager.boot -import org.enso.loggingservice.LogLevel +//import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level import java.io.File import java.nio.file.Path - import scala.concurrent.duration.FiniteDuration object configuration { @@ -18,7 +18,7 @@ object configuration { * @param profilingTime the time limiting the profiling duration */ case class MainProcessConfig( - logLevel: LogLevel, + logLevel: Level, profilingEventsLogPath: Option[Path], profilingPath: Option[Path], profilingTime: Option[FiniteDuration] diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala index c97abe131f2a..2329f447e8c9 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala @@ -4,7 +4,7 @@ import akka.actor.ActorRef import com.typesafe.scalalogging.Logger import org.apache.commons.lang3.concurrent.BasicThreadFactory import org.enso.logger.masking.Masking -import org.enso.loggingservice.LoggingServiceManager +import org.enso.logging.LoggingServiceManager import org.enso.projectmanager.service.versionmanagement.RuntimeVersionManagerFactory import org.enso.runtimeversionmanager.config.GlobalRunnerConfigurationManager import org.enso.runtimeversionmanager.runner.{LanguageServerOptions, Runner} @@ -75,6 +75,7 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor { val inheritedLogLevel = LoggingServiceManager.currentLogLevelForThisApplication() + //LoggingCollector.currentLogLevelForThisApplication() val options = LanguageServerOptions( rootId = descriptor.rootId, interface = descriptor.networkConfig.interface, diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala index 5e3d06e91f14..8b31da1707ae 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerDescriptor.scala @@ -1,14 +1,13 @@ package org.enso.projectmanager.infrastructure.languageserver -import akka.http.scaladsl.model.Uri import nl.gn0s1s.bump.SemVer import org.enso.projectmanager.boot.configuration.NetworkConfig import org.enso.projectmanager.versionmanagement.DistributionConfiguration import org.enso.runtimeversionmanager.runner.JVMSettings +import java.net.URI import java.nio.file.Path import java.util.UUID - import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration @@ -32,7 +31,7 @@ import scala.concurrent.duration.FiniteDuration * logging service has been fully set-up; * if the child component should connect * to the logging service, it should - * contain the Uri to connect to + * contain the URI to connect to * @param skipGraalVMUpdater indicates if the check and installation of GraalVM * should be skipped */ @@ -48,6 +47,6 @@ case class LanguageServerDescriptor( profilingEventsLogPath: Option[Path], profilingPath: Option[Path], profilingTime: Option[FiniteDuration], - deferredLoggingServiceEndpoint: Future[Option[Uri]], + deferredLoggingServiceEndpoint: Future[Option[URI]], skipGraalVMUpdater: Boolean ) diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/requesthandler/LoggingServiceEndpointRequestHandler.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/requesthandler/LoggingServiceEndpointRequestHandler.scala index f5dbea52ee0b..a7b146d9b7ca 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/requesthandler/LoggingServiceEndpointRequestHandler.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/requesthandler/LoggingServiceEndpointRequestHandler.scala @@ -1,7 +1,6 @@ package org.enso.projectmanager.requesthandler import akka.actor.{Actor, ActorRef, Cancellable, Props, Status} -import akka.http.scaladsl.model.Uri import akka.pattern.pipe import com.typesafe.scalalogging.LazyLogging import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult} @@ -12,6 +11,7 @@ import org.enso.projectmanager.protocol.ProjectManagementApi.{ import org.enso.projectmanager.service.LoggingServiceDescriptor import org.enso.projectmanager.util.UnhandledLogging +import java.net.URI import scala.concurrent.duration.FiniteDuration /** A request handler for `logging-service/get-endpoint` commands. @@ -88,7 +88,7 @@ class LoggingServiceEndpointRequestHandler( context.stop(self) } - private case class LoggingServiceInitialized(endpoint: Option[Uri]) + private case class LoggingServiceInitialized(endpoint: Option[URI]) } object LoggingServiceEndpointRequestHandler { diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/service/LoggingServiceDescriptor.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/service/LoggingServiceDescriptor.scala index d4f6298e5870..5d0cc6bb5d66 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/service/LoggingServiceDescriptor.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/service/LoggingServiceDescriptor.scala @@ -1,7 +1,6 @@ package org.enso.projectmanager.service -import akka.http.scaladsl.model.Uri - +import java.net.URI import scala.concurrent.Future /** A service descriptor that provides information on the logging service setup. @@ -12,5 +11,5 @@ trait LoggingServiceDescriptor { * initialized or None if the logging service does not expect incoming * connections. */ - def getEndpoint: Future[Option[Uri]] + def getEndpoint: Future[Option[URI]] } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala index 35ff3b5230fd..76ee6d0b1ea4 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala @@ -2,16 +2,19 @@ package org.enso.projectmanager.util import akka.actor.Actor import com.typesafe.scalalogging.LazyLogging -import org.enso.loggingservice.LogLevel +import org.enso.logger.akka.AkkaConverter +//import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level trait UnhandledLogging extends LazyLogging { this: Actor => - private val akkaLogLevel = LogLevel + private val akkaLogLevel = AkkaConverter //LogLevel .fromString(context.system.settings.LogLevel) - .getOrElse(LogLevel.Error) + .orElse(Level.ERROR) override def unhandled(message: Any): Unit = { - if (implicitly[Ordering[LogLevel]].lteq(LogLevel.Warning, akkaLogLevel)) { + //if (implicitly[Ordering[LogLevel]].lteq(LogLevel.Warning, akkaLogLevel)) { + if (Level.WARN.toInt <= akkaLogLevel.toInt) { logger.warn("Received unknown message: {}", message.getClass) } } diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala index 8e504b6b3283..f0e74b8a227c 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala @@ -16,8 +16,8 @@ import org.enso.editions.Editions import org.enso.cli.OS import org.enso.jsonrpc.test.JsonRpcServerTestKit import org.enso.jsonrpc.{ClientControllerFactory, ProtocolFactory} -import org.enso.loggingservice.printers.StderrPrinterWithColors -import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} +//import org.enso.loggingservice.printers.StderrPrinterWithColors +//import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} import org.enso.pkg.{Config, PackageManager} import org.enso.projectmanager.boot.Globals.{ConfigFilename, ConfigNamespace} import org.enso.projectmanager.boot.configuration._ @@ -47,6 +47,7 @@ import org.enso.projectmanager.test.{ObservableGenerator, ProgrammableClock} import org.enso.runtimeversionmanager.components.GraalVMVersion import org.enso.runtimeversionmanager.test.FakeReleases import org.scalatest.BeforeAndAfterAll +import org.slf4j.event.Level import pureconfig.ConfigSource import pureconfig.generic.auto._ import zio.interop.catz.core._ @@ -85,7 +86,7 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { val processConfig: MainProcessConfig = MainProcessConfig( - logLevel = if (debugLogs) LogLevel.Trace else LogLevel.Off, + logLevel = if (debugLogs) Level.TRACE else Level.ERROR, profilingPath = profilingPath, profilingTime = None, profilingEventsLogPath = None @@ -239,12 +240,13 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { setupEditions() if (debugLogs) { - LoggingServiceManager.setup( + // TODO + /*LoggingServiceManager.setup( LoggerMode.Local( Seq(StderrPrinterWithColors.colorPrinterIfAvailable(true)) ), LogLevel.Trace - ) + )*/ } engineToInstall.foreach(preInstallEngine) diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala index d7f5b8376dd7..cfda25d2e9a9 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala @@ -2,7 +2,9 @@ package org.enso.runtimeversionmanager.cli import akka.http.scaladsl.model.{IllegalUriException, Uri} import org.enso.cli.arguments.{Argument, OptsParseError} -import org.enso.loggingservice.LogLevel +import org.enso.logger.Converter +//import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level object Arguments { implicit val uriArgument: Argument[Uri] = (string: String) => @@ -13,9 +15,11 @@ object Arguments { Left(OptsParseError(s"`$string` is not a valid Uri: $error.")) } - implicit val logLevelArgument: Argument[LogLevel] = (string: String) => { - val provided = string.toLowerCase - LogLevel.allLevels + implicit val logLevelArgument: Argument[Level] = (string: String) => { + val provided = Converter.backwardCompatibleName(string.toLowerCase) + Level + .values() + //LogLevel.allLevels .find(_.toString.toLowerCase == provided) .toRight( OptsParseError(s"`$string` is not a valid log level.") diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala index 2e9135578f6f..d398089f7903 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala @@ -1,13 +1,15 @@ package org.enso.runtimeversionmanager.runner -import akka.http.scaladsl.model.Uri import com.typesafe.scalalogging.Logger import nl.gn0s1s.bump.SemVer import org.enso.distribution.{DistributionManager, Environment} import org.enso.editions.updater.EditionManager import org.enso.editions.{DefaultEnsoVersion, SemVerEnsoVersion} import org.enso.logger.masking.MaskedString -import org.enso.loggingservice.LogLevel +import org.slf4j.event.Level + +import java.net.URI +//import org.enso.loggingservice.LogLevel import org.enso.runtimeversionmanager.components.Manifest.JVMOptionsContext import org.enso.runtimeversionmanager.components.{ Engine, @@ -31,7 +33,7 @@ class Runner( globalConfigurationManager: GlobalRunnerConfigurationManager, editionManager: EditionManager, environment: Environment, - loggerConnection: Future[Option[Uri]] + loggerConnection: Future[Option[URI]] ) { /** The current working directory that is a starting point when checking if @@ -86,7 +88,7 @@ class Runner( options: LanguageServerOptions, project: Project, versionOverride: Option[SemVer], - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = { @@ -107,7 +109,7 @@ class Runner( options: LanguageServerOptions, projectPath: String, version: SemVer, - logLevel: LogLevel, + logLevel: Level, logMasking: Boolean, additionalArguments: Seq[String] ): Try[RunSettings] = diff --git a/tools/simple-library-server/package-lock.json b/tools/simple-library-server/package-lock.json index 946531acfd69..e040ca7f67da 100644 --- a/tools/simple-library-server/package-lock.json +++ b/tools/simple-library-server/package-lock.json @@ -1,956 +1,8 @@ { "name": "simple-library-server", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "simple-library-server", - "version": "1.0.0", - "license": "Apache-2.0", - "dependencies": { - "compression": "^1.7.4", - "express": "^4.17.1", - "multer": "^1.4.2", - "semver": "^7.5.2", - "yargs": "^17.0.1" - }, - "engines": { - "node": ">=14.17.2" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", - "dependencies": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" - }, - "node_modules/dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==", - "dependencies": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/multer": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", - "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", - "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", - "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - } - }, "dependencies": { "accepts": { "version": "1.3.8", @@ -1553,11 +605,6 @@ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==" }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1568,6 +615,11 @@ "strip-ansi": "^6.0.1" } }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", From 0bf5abc07e7b88fe3b11aa47ee0d78b3dcf1e6cd Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 10 Aug 2023 22:27:11 +0200 Subject: [PATCH 02/31] format sbt --- build.sbt | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/build.sbt b/build.sbt index 0f2d3b637d42..c075a27390d8 100644 --- a/build.sbt +++ b/build.sbt @@ -683,8 +683,8 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "org.slf4j" % "slf4j-api" % slf4jVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test ) ) @@ -698,9 +698,9 @@ lazy val `logging-socket-collector` = project libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp ) ) @@ -713,14 +713,15 @@ lazy val `logging-jutil` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, + "org.slf4j" % "slf4j-api" % slf4jVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp ) - ).dependsOn(`logging-logback`) + ) + .dependsOn(`logging-logback`) lazy val `logging-logback` = project .in(file("lib/scala/logging-logback")) @@ -729,15 +730,14 @@ lazy val `logging-logback` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, + "org.slf4j" % "slf4j-api" % slf4jVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, - "com.typesafe" % "config" % typesafeConfigVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, + "com.typesafe" % "config" % typesafeConfigVersion, akkaHttp ) ) - lazy val `logging-utils-akka` = project .in(file("lib/scala/logging-utils-akka")) .configs(Test) @@ -745,9 +745,9 @@ lazy val `logging-utils-akka` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, + "org.slf4j" % "slf4j-api" % slf4jVersion, "com.typesafe.akka" %% "akka-actor" % akkaVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test + "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ) @@ -1164,7 +1164,7 @@ lazy val `language-server` = (project in file("engine/language-server")) "org.scalatest" %% "scalatest" % scalatestVersion % Test, "org.scalacheck" %% "scalacheck" % scalacheckVersion % Test, "org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided", - "org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion, + "org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion ), Test / testOptions += Tests .Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"), @@ -1775,7 +1775,7 @@ lazy val `engine-runner` = project .dependsOn(`library-manager`) .dependsOn(`language-server`) .dependsOn(`polyglot-api`) - //.dependsOn(`logging-service`) +//.dependsOn(`logging-service`) lazy val launcher = project .in(file("engine/launcher")) @@ -2072,7 +2072,7 @@ lazy val `library-manager` = project .dependsOn(`distribution-manager`) .dependsOn(downloader) .dependsOn(testkit % Test) - //.dependsOn(`logging-service` % Test) +//.dependsOn(`logging-service` % Test) lazy val `library-manager-test` = project .in(file("lib/scala/library-manager-test")) @@ -2088,7 +2088,7 @@ lazy val `library-manager-test` = project .dependsOn(`library-manager`) .dependsOn(`logging-utils` % "test->test") .dependsOn(testkit) - //.dependsOn(`logging-service`) +//.dependsOn(`logging-service`) lazy val `connected-lock-manager` = project .in(file("lib/scala/connected-lock-manager")) From 1591ff61232e35170cc7f08d9efeb77c6cc460bb Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 14 Aug 2023 15:04:00 +0200 Subject: [PATCH 03/31] Fix native image build, many tests wip --- build.sbt | 21 ++---- .../resources/language-server.logback.xml | 9 --- .../boot/LanguageServerComponent.scala | 18 +++-- .../enso/languageserver/boot/MainModule.scala | 2 +- .../CompilerBasedDependencyExtractor.scala | 2 +- .../src/test/resources/application.conf | 23 +++++- .../src/test/resources/logback-test.xml | 24 ++++++ .../enso/launcher/cli/LauncherLogging.scala | 8 +- .../components/LauncherRunnerSpec.scala | 5 +- .../org/enso/runner/reflect-config.json | 4 + .../src/main/resources/runner.logback.xml | 21 ++++++ .../org/enso/runner/ContextFactory.scala | 2 +- .../src/main/scala/org/enso/runner/Main.scala | 10 +-- .../scala/org/enso/runner/RunnerLogging.scala | 74 +++++++++---------- .../org/enso/logger/JavaLoggingForwarder.java | 9 +++ .../org/enso/logger/LoggerContextSetup.java | 65 ++++++++++++---- .../enso/logger/config/LoggingService.java | 25 ++++++- .../java/org/enso/logging/ExternalLogger.java | 28 +++++++ .../org/enso/logging/ForwardToServer.java | 11 --- .../src/main/java/org/enso/logging/Local.java | 23 ++++++ .../main/resources/logging-server.logback.xml | 4 +- .../enso/logging/LoggingCollectorHelper.scala | 11 ++- .../enso/logging/LoggingServiceManager.scala | 26 +++++-- .../projectmanager/TestLoggingService.scala | 8 +- .../protocol/LoggingServiceEndpointSpec.scala | 4 +- project/NativeImage.scala | 3 +- 26 files changed, 310 insertions(+), 130 deletions(-) create mode 100644 engine/language-server/src/test/resources/logback-test.xml create mode 100644 engine/runner/src/main/resources/runner.logback.xml create mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java create mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java diff --git a/build.sbt b/build.sbt index c075a27390d8..193bfd72a61f 100644 --- a/build.sbt +++ b/build.sbt @@ -921,6 +921,8 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .buildNativeImage( "project-manager", staticOnLinux = true, + additionalOptions = Seq( + "-H:IncludeResources=.*logback.xml$"), initializeAtRuntime = Seq( "scala.util.Random", "zio.internal.ZScheduler$$anon$4", @@ -1734,6 +1736,7 @@ lazy val `engine-runner` = project additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", "-H:IncludeResources=.*Main.enso$", + "-H:IncludeResources=.*logback.xml$", "--macro:truffle", "--language:js", // "-g", @@ -1744,19 +1747,16 @@ lazy val `engine-runner` = project mainClass = Option("org.enso.runner.Main"), cp = Option("runtime.jar"), initializeAtRuntime = Seq( - // Note [WSLoggerManager Shutdown Hook] - "org.enso.loggingservice.WSLoggerManager$", "org.jline.nativ.JLineLibrary", "io.methvin.watchservice.jna.CarbonAPI", "org.enso.syntax2.Parser", - //"org.enso.loggingservice", "zio.internal.ZScheduler$$anon$4", "sun.awt", "sun.java2d", "sun.font", "java.awt", "com.sun.imageio", - "akka.http" + "akka.http", ) ) .dependsOn(installNativeImage) @@ -1775,7 +1775,7 @@ lazy val `engine-runner` = project .dependsOn(`library-manager`) .dependsOn(`language-server`) .dependsOn(`polyglot-api`) -//.dependsOn(`logging-service`) + .dependsOn(`logging-logback`) lazy val launcher = project .in(file("engine/launcher")) @@ -1798,12 +1798,9 @@ lazy val launcher = project staticOnLinux = true, additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", - "-H:IncludeResources=.*Main.enso$" + "-H:IncludeResources=.*Main.enso$", + "-H:IncludeResources=.*logback.xml$" ), - initializeAtRuntime = Seq( - // Note [WSLoggerManager Shutdown Hook] - "org.enso.loggingservice.WSLoggerManager$" - ) ) .dependsOn(installNativeImage) .dependsOn(assembly) @@ -1843,7 +1840,6 @@ lazy val launcher = project .dependsOn(pkg) .dependsOn(`logging-utils` % "test->test") .dependsOn(`logging-socket-collector`) - //.dependsOn(`logging-service`) .dependsOn(`distribution-manager` % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -2088,7 +2084,6 @@ lazy val `library-manager-test` = project .dependsOn(`library-manager`) .dependsOn(`logging-utils` % "test->test") .dependsOn(testkit) -//.dependsOn(`logging-service`) lazy val `connected-lock-manager` = project .in(file("lib/scala/connected-lock-manager")) @@ -2123,7 +2118,6 @@ lazy val `runtime-version-manager` = project ) .dependsOn(pkg) .dependsOn(downloader) - //.dependsOn(`logging-service`) .dependsOn(cli) .dependsOn(`version-output`) .dependsOn(`edition-updater`) @@ -2147,7 +2141,6 @@ lazy val `runtime-version-manager-test` = project .value ) .dependsOn(`runtime-version-manager`) - //.dependsOn(`logging-service`) .dependsOn(testkit) .dependsOn(cli) .dependsOn(`distribution-manager`) diff --git a/engine/language-server/src/main/resources/language-server.logback.xml b/engine/language-server/src/main/resources/language-server.logback.xml index b3b8ce4bfc4d..82b757800da8 100644 --- a/engine/language-server/src/main/resources/language-server.logback.xml +++ b/engine/language-server/src/main/resources/language-server.logback.xml @@ -19,15 +19,6 @@ true - - diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala index b25597b655cd..74b15467d85d 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala @@ -39,13 +39,7 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) /** @inheritdoc */ override def start(): Future[ComponentStarted.type] = { - val logConfig = - this.getClass.getResourceAsStream("/language-server.logback.xml") - if (logConfig != null) { - LoggerContextSetup.setupLogging(logLevel, "language-server", logConfig); - } else { - System.err.println("Unable to set up logging for Language Server.") - } + setupLogging() logger.info("Starting Language Server...") val sampler = startSampling(config) val module = new MainModule(config, logLevel) @@ -75,6 +69,16 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) } yield ComponentStarted } + private def setupLogging(): Unit = { + val logConfig = + this.getClass.getResourceAsStream("/language-server.logback.xml") + if (logConfig != null) { + LoggerContextSetup.setup(logLevel, "language-server", logConfig); + } else { + System.err.println("Unable to set up logging for Language Server.") + } + } + /** Start the application sampling. */ private def startSampling(config: LanguageServerConfig): MethodsSampler = { val sampler = config.profilingConfig.profilingPath match { 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 b6a7b8dc013f..6b3114b6cc2c 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 @@ -309,7 +309,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { .out(stdOut) .err(stdErr) .in(stdIn) - .logHandler(new JavaLoggingForwarder(6000)) + .logHandler(new JavaLoggingForwarder()) .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/libraries/CompilerBasedDependencyExtractor.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala index 33b69b1ee4c8..4a6cf465edd9 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala @@ -65,7 +65,7 @@ class CompilerBasedDependencyExtractor(logLevel: Level) RuntimeOptions.LOG_LEVEL, Converter.toJavaLevel(logLevel).getName ) - .logHandler(new JavaLoggingForwarder(6000)) + .logHandler(new JavaLoggingForwarder()) .build new PolyglotContext(context) } diff --git a/engine/language-server/src/test/resources/application.conf b/engine/language-server/src/test/resources/application.conf index 08bfee1a55b4..6160d14db9ee 100644 --- a/engine/language-server/src/test/resources/application.conf +++ b/engine/language-server/src/test/resources/application.conf @@ -5,7 +5,28 @@ akka.loglevel = "ERROR" akka.test.timefactor = ${?CI_TEST_TIMEFACTOR} akka.test.single-expect-default = 5s -logging-service.test-log-level = warning searcher.db.numThreads = 1 searcher.db.properties.journal_mode = "memory" + +logging-service { + logger { + akka.actor = info + akka.event = error + akka.routing = error + akka.io = error + akka.stream = error + slick.jdbc.JdbcBackend.statement = error # log SQL queries on debug level + slick."*" = error + org.eclipse.jgit = error + io.methvin.watcher = error + org.enso.languageserver.protocol.json.JsonConnectionController = debug # very verbose in trace mode + org.enso.jsonrpc.JsonRpcServer = debug # verbose in trace mode + org.enso.languageserver.runtime.RuntimeConnector = debug + } + appender = "forward-via-socket" + server { + hostname = "localhost" + port = 6000 + } +} diff --git a/engine/language-server/src/test/resources/logback-test.xml b/engine/language-server/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..3237c2767743 --- /dev/null +++ b/engine/language-server/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + + + [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n + + --> + + + + + + + + + + + + + + diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 3b799fc19e71..c67ed609da8c 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -5,6 +5,7 @@ import org.enso.launcher.distribution.DefaultManagers import org.slf4j.event.Level import org.enso.logging.LoggingCollectorHelper +import org.enso.logging.LoggingServiceManager import scala.concurrent.ExecutionContext.Implicits.global @@ -35,10 +36,9 @@ object LauncherLogging extends LoggingCollectorHelper { */ def prepareForUninstall(): Unit = { //colorMode: ColorMode): Unit = { // TODO - /*waitForSetup() + waitForSetup() - LoggingServiceManager.replaceWithFallback(printers = - Seq(stderrPrinter(colorMode, printExceptions = true)) - )*/ + // TODO: fetch actual log level + LoggingServiceManager.fallbackToLocalConsole(defaultLogLevel, "launcher") } } diff --git a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala index b5078fc18135..3097e33fa494 100644 --- a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala +++ b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala @@ -1,8 +1,8 @@ package org.enso.launcher.components import java.nio.file.{Files, Path} +import java.net.URI import java.util.UUID -import akka.http.scaladsl.model.Uri import nl.gn0s1s.bump.SemVer import org.enso.distribution.FileSystem.PathSyntax import org.enso.editions.updater.EditionManager @@ -10,7 +10,6 @@ import org.enso.runtimeversionmanager.config.GlobalRunnerConfigurationManager import org.enso.runtimeversionmanager.runner._ import org.enso.runtimeversionmanager.test.RuntimeVersionManagerTest import org.enso.launcher.project.ProjectManager -//import org.enso.loggingservice.{LogLevel, TestLogger} import org.enso.logger.TestLogger import org.slf4j.event.Level @@ -24,7 +23,7 @@ import scala.concurrent.Future class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { private val defaultEngineVersion = SemVer(0, 0, 0, Some("default")) - private val fakeUri = Uri("ws://test:1234/") + private val fakeUri = URI.create("ws://test:1234/") def makeFakeRunner( cwdOverride: Option[Path] = None, diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index a1216566d6ce..976d3e93f217 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -212,6 +212,10 @@ "name": "org.apache.commons.compress.archivers.zip.X7875_NewUnix", "methods": [{ "name": "", "parameterTypes": [] }] }, + { + "name": "org.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField", + "methods": [{ "name": "", "parameterTypes": [] }] + }, { "name": "org.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField", "methods": [{ "name": "", "parameterTypes": [] }] diff --git a/engine/runner/src/main/resources/runner.logback.xml b/engine/runner/src/main/resources/runner.logback.xml new file mode 100644 index 000000000000..e97fd581a711 --- /dev/null +++ b/engine/runner/src/main/resources/runner.logback.xml @@ -0,0 +1,21 @@ + + + + + + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + + + ${logging-server.host} + ${logging-server.port} + 10000 + true + + + + + + diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 8c749e64746a..4bb987e5609e 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -87,7 +87,7 @@ class ContextFactory { RuntimeOptions.LOG_LEVEL, logLevelName ) - .logHandler(new JavaLoggingForwarder(6000)) + .logHandler(new JavaLoggingForwarder()) .build new PolyglotContext(context) } diff --git a/engine/runner/src/main/scala/org/enso/runner/Main.scala b/engine/runner/src/main/scala/org/enso/runner/Main.scala index a4e071c43971..485aa8a84306 100644 --- a/engine/runner/src/main/scala/org/enso/runner/Main.scala +++ b/engine/runner/src/main/scala/org/enso/runner/Main.scala @@ -1,6 +1,6 @@ package org.enso.runner -import akka.http.scaladsl.model.{IllegalUriException, Uri} +import akka.http.scaladsl.model.{IllegalUriException} import buildinfo.Info import cats.implicits._ import com.typesafe.scalalogging.Logger @@ -15,13 +15,13 @@ import org.enso.languageserver.boot.{ } import org.enso.libraryupload.LibraryUploader.UploadFailedError import org.slf4j.event.Level -//import org.enso.loggingservice.LogLevel import org.enso.pkg.{Contact, PackageManager, Template} import org.enso.polyglot.{HostEnsoUtils, LanguageInfo, Module, PolyglotContext} import org.enso.version.VersionDescription import org.graalvm.polyglot.PolyglotException import java.io.File +import java.net.URI import java.nio.file.{Path, Paths} import java.util.{HashMap, UUID} import scala.Console.err @@ -1013,9 +1013,9 @@ object Main { /** Parses an URI that specifies the logging service connection. */ - def parseUri(string: String): Uri = + def parseUri(string: String): URI = try { - Uri(string) + URI.create(string) } catch { case _: IllegalUriException => System.err.println(s"`$string` is not a valid URI.") @@ -1180,7 +1180,7 @@ object Main { } } - /** Checks whether IR caching should be enabled.o + /** Checks whether IR caching should be enabled. * * The (mutually exclusive) flags can control it explicitly, otherwise it * defaults to off in development builds and on in production builds. diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index 1feae6a6eead..9fe8f9438953 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -1,16 +1,16 @@ package org.enso.runner -import akka.http.scaladsl.model.Uri +import java.net.URI import com.typesafe.scalalogging.Logger import org.enso.logger.masking.Masking import scala.annotation.unused -//import org.enso.loggingservice.printers.StderrPrinter import org.slf4j.event.Level -//import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} -import scala.concurrent.Future import scala.util.{Failure, Success} +import scala.concurrent.Future +import java.io.InputStream +import org.enso.logger.LoggerContextSetup /** Manages setting up the logging service within the runner. */ @@ -27,22 +27,25 @@ object RunnerLogging { * @param logMasking switches log masking on and off */ def setup( - connectionUri: Option[Uri], + connectionUri: Option[URI], logLevel: Level, logMasking: Boolean ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) + val loggingConfig = this.getClass.getResourceAsStream("/runner.logback.xml") val loggerSetup = connectionUri match { case Some(uri) => - // TODO - /*LoggingServiceManager - .setup( - LoggerMode.Client(uri), - logLevel - )*/ - Future - .successful(()) + Future( + LoggerContextSetup.setup( + logLevel, + "runner", + loggingConfig, + "forward-via-socket", + uri.getHost(), + uri.getPort() + ) + ) .map { _ => logger.trace("Connected to logging service at [{}].", uri) } @@ -51,10 +54,10 @@ object RunnerLogging { "Failed to connect to the logging service server, " + "falling back to local logging." ) - setupLocalLogger(logLevel) + setupLocalLogger(logLevel, loggingConfig) } case None => - setupLocalLogger(logLevel) + setupLocalLogger(logLevel, loggingConfig) } loggerSetup.onComplete { case Failure(exception) => @@ -64,34 +67,27 @@ object RunnerLogging { } } - private def setupLocalLogger(@unused logLevel: Level): Future[Unit] = { - // TODO - Future.successful(()) + private def setupLocalLogger( + @unused logLevel: Level, + logbackConfig: InputStream + ): Future[Unit] = { + Future.successful( + LoggerContextSetup.setup( + logLevel, + "runner", + logbackConfig, + "console", + null, + 0 + ) + ) } - /*LoggingServiceManager - .setup( - LoggerMode.Local( - Seq(StderrPrinter.create(printExceptions = true)) - ), - logLevel - )*/ - - /*private def setupLocalLogger(logLevel: LogLevel): Future[Unit] = - LoggingServiceManager - .setup( - LoggerMode.Local( - Seq(StderrPrinter.create(printExceptions = true)) - ), - logLevel - )*/ - private val logger = Logger[RunnerLogging.type] /** Shuts down the logging service gracefully. */ - def tearDown(): Unit = - // TODO - () - //LoggingServiceManager.tearDown() + def tearDown(): Unit = { + LoggerContextSetup.teardown() + } } diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java index 07d0fd336f74..f5e752f199c6 100644 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java @@ -18,6 +18,7 @@ import java.util.logging.Handler; import java.util.logging.LogRecord; import org.enso.logger.config.LoggingService; +import org.enso.logger.config.Server; /** * JavaLoggingForwarder will forward all java.util.logging.LogRecord to a Logback's appender. @@ -30,6 +31,14 @@ public class JavaLoggingForwarder extends Handler { private ch.qos.logback.core.Appender appender; private boolean started; + public JavaLoggingForwarder() { + this(LoggingService.parseConfig().getServer()); + } + + public JavaLoggingForwarder(Server config) { + this(config.hostname(), config.port()); + } + public JavaLoggingForwarder(int port) { this("localhost", port); } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java index 8747e372ad94..b10d5f08dc24 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java @@ -5,26 +5,59 @@ import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.util.StatusPrinter; import java.io.InputStream; +import org.enso.logger.config.Loggers; import org.enso.logger.config.LoggingService; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; public class LoggerContextSetup { - public static Boolean setupLogging( - Level level, String componentName, InputStream customLogConfig) { - return setupLogging(level, componentName, customLogConfig, LoggingService.parseConfig()); + public static Boolean setup(Level level, String componentName, InputStream customLogConfig) { + return setup(level, componentName, customLogConfig, LoggingService.parseConfig()); } - public static Boolean setupLogging( + public static Boolean setup( Level logLevel, String componentName, InputStream customLogConfig, LoggingService appConfig) { + var serverConfig = appConfig.getServer(); + var appenderName = appConfig.getAppender(); + return setup( + logLevel, + componentName, + customLogConfig, + appenderName, + serverConfig.hostname(), + serverConfig.port(), + appConfig.getLoggers()); + } + + public static Boolean setup( + Level logLevel, + String componentName, + InputStream customLogConfig, + String appenderName, + String hostname, + int port) { + return setup(logLevel, componentName, customLogConfig, appenderName, hostname, port, null); + } + + public static Boolean setup( + Level logLevel, + String componentName, + InputStream customLogConfig, + String appenderName, + String hostname, + int port, + Loggers loggers) { var context = (LoggerContext) LoggerFactory.getILoggerFactory(); var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); - System.setProperty(componentName + ".appender", appConfig.getAppender()); - var serverConfig = appConfig.getServer(); - System.setProperty("logging-server.host", serverConfig.hostname()); - System.setProperty("logging-server.port", String.valueOf(serverConfig.port())); + System.setProperty(componentName + ".appender", appenderName); + if (hostname != null) { + System.setProperty("logging-server.host", hostname); + } + if (port != 0) { + System.setProperty("logging-server.port", String.valueOf(port)); + } try { var configurator = new JoranConfigurator(); configurator.setContext(context); @@ -33,14 +66,15 @@ public static Boolean setupLogging( context.reset(); configurator.doConfigure(customLogConfig); var rootLogger = context.getLogger("root"); - var appender = rootLogger.getAppender(appConfig.getAppender()); + var appender = rootLogger.getAppender(appenderName); if (appender == null) { System.err.println( - "Failed to apply custom log levels for application loggers' in " - + appConfig.getAppender()); + "Failed to apply custom log levels for application loggers' in " + appenderName); } else { - var filter = ApplicationFilter.fromLoggers(appConfig.getLoggers()); - appender.addFilter(filter); + if (loggers != null) { + var filter = ApplicationFilter.fromLoggers(loggers); + appender.addFilter(filter); + } } return true; } catch (JoranException je) { @@ -50,4 +84,9 @@ public static Boolean setupLogging( return false; } } + + public static void teardown() { + var context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.stop(); + } } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java index cc61962799ee..8745b07a5c49 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java @@ -1,21 +1,27 @@ package org.enso.logger.config; +import com.typesafe.config.Config; +import com.typesafe.config.ConfigException; import com.typesafe.config.ConfigFactory; +import java.util.Optional; public class LoggingService { public static final String configurationRoot = "logging-service"; public static final String serverKey = "server"; public static final String loggersKey = "logger"; - public static final String appenderKey = "appender"; + public static final String testLogLevelKey = "test-log-level"; private Loggers loggers; private String appender; + private Optional testLogLevel; private Server server; - private LoggingService(Loggers loggers, String appender, Server server) { + private LoggingService( + Loggers loggers, Optional testLogLevel, String appender, Server server) { this.loggers = loggers; this.appender = appender; + this.testLogLevel = testLogLevel; this.server = server; } @@ -24,7 +30,8 @@ public static LoggingService parseConfig() { var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); Server server = Server.parse(root.getConfig(serverKey)); Loggers loggers = Loggers.parse(root.getConfig(loggersKey)); - return new LoggingService(loggers, root.getString(appenderKey), server); + return new LoggingService( + loggers, getStringOpt(testLogLevelKey, root), root.getString(appenderKey), server); } public Loggers getLoggers() { @@ -35,6 +42,18 @@ public String getAppender() { return appender; } + private static Optional getStringOpt(String key, Config config) { + try { + return Optional.ofNullable(config.getString(key)); + } catch (ConfigException.Missing missing) { + return Optional.empty(); + } + } + + public Optional getTestLogLevel() { + return testLogLevel; + } + public Server getServer() { return server; } diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java new file mode 100644 index 000000000000..49769a13b2da --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java @@ -0,0 +1,28 @@ +package org.enso.logging; + +import java.net.ConnectException; +import java.net.URI; +import org.slf4j.event.Level; + +public class ExternalLogger extends LoggingService { + private URI targetURI; + + public ExternalLogger(URI uri) { + this.targetURI = uri; + } + + public void connect() throws ConnectException { + /* SocketAppender socketAppender = new SocketAppender(); + socketAppender.setPort(port); + socketAppender.setRemoteHost(host); + socketAppender.setIncludeCallerData(false);*/ + throw new ConnectException("failed to connect to " + targetURI); + } + + @Override + public void teardown() {} + + public boolean fallbackToConsole(Level level) { + return false; + } +} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java index f84a5101c8d9..bd0d5cbe0248 100644 --- a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java +++ b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java @@ -51,17 +51,6 @@ public void forwardToSocket(Level level, String targetHostname, int targetPort) logServer.start(); } - public void logToConsole(Level level) throws JoranException { - var lc = new LoggerContext(); - var configurator = new JoranConfigurator(); - configurator.setContext(lc); - System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); - System.setProperty("logging-server.appender", consoleAppender); - configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); - logServer = new SimpleSocketServer(lc, port); - logServer.start(); - } - public boolean isSetup() { return logServer != null; } diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java new file mode 100644 index 000000000000..3ac60ad14002 --- /dev/null +++ b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java @@ -0,0 +1,23 @@ +package org.enso.logging; + +import org.enso.logger.LoggerContextSetup; +import org.slf4j.event.Level; + +public class Local extends LoggingService { + + private String componentName; + + public Local(String componentName) { + this.componentName = componentName; + } + + @Override + public void teardown() { + LoggerContextSetup.teardown(); + } + + public void start(Level logLevel) { + var logConfig = this.getClass().getResourceAsStream("/" + componentName + ".logback.xml"); + LoggerContextSetup.setup(logLevel, componentName, logConfig, "console", null, 0); + } +} diff --git a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml index e1c3b8db6a89..9f85429d1771 100644 --- a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml +++ b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml @@ -11,12 +11,12 @@ false true - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala index 7b8c555ebe9c..201e477e75a0 100644 --- a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala +++ b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala @@ -14,6 +14,9 @@ import scala.util.{Failure, Success} import org.enso.logger.LoggerContextSetup import org.enso.logger.config.LoggingService +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt + abstract class LoggingCollectorHelper(implicit executionContext: ExecutionContext ) { @@ -93,7 +96,7 @@ abstract class LoggingCollectorHelper(implicit .map(conf => Boolean.unbox( LoggerContextSetup - .setupLogging(actualLogLevel, logComponentName, conf) + .setup(actualLogLevel, logComponentName, conf) ) ) .getOrElse(false) @@ -114,7 +117,7 @@ abstract class LoggingCollectorHelper(implicit .map(conf => Boolean.unbox( LoggerContextSetup - .setupLogging(actualLogLevel, logComponentName, conf) + .setup(actualLogLevel, logComponentName, conf) ) ) .getOrElse(false) @@ -123,4 +126,8 @@ abstract class LoggingCollectorHelper(implicit } } } + + def waitForSetup(): Unit = { + Await.ready(loggingServiceEndpointPromise.future, 5.seconds) + } } diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala index 8e1ea623224e..50791d8a34d4 100644 --- a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -1,7 +1,6 @@ package org.enso.logging import java.net.URI -import scala.util.{Success, Try} import org.slf4j.event.Level import java.nio.file.Path import scala.concurrent.Future @@ -32,15 +31,28 @@ object LoggingServiceManager { } } - def fallbackToLocalConsole(logLevel: Level): Try[Unit] = { + def setupConnection(logLevel: Level, uri: URI)(implicit + ec: ExecutionContext + ): Future[Unit] = { if (loggingService != null) { - loggingService.teardown() + throw new RuntimeException("logging service already setup") + } else { + currentLevel = logLevel + val client = new ExternalLogger(uri) + loggingService = client + Future { + client.connect() + } } - System.setProperty("logging-server.logLevel", logLevel.toString.toLowerCase) - System.setProperty("logging-server.appender", "console") + } - // TODO: start console appender - Success(()) + def fallbackToLocalConsole(logLevel: Level, componentName: String): Unit = { + if (loggingService != null) { + loggingService.teardown() + } + val local = new Local(componentName) + loggingService = local + local.start(logLevel) } def teardown(): Unit = { diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/TestLoggingService.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/TestLoggingService.scala index b63065561b49..fa65470fd5d7 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/TestLoggingService.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/TestLoggingService.scala @@ -1,17 +1,17 @@ package org.enso.projectmanager -import akka.http.scaladsl.model.Uri import org.enso.projectmanager.service.LoggingServiceDescriptor +import java.net.URI import scala.concurrent.Future class TestLoggingService extends LoggingServiceDescriptor { - private var currentFuture: Future[Option[Uri]] = Future.successful(None) + private var currentFuture: Future[Option[URI]] = Future.successful(None) - override def getEndpoint: Future[Option[Uri]] = currentFuture + override def getEndpoint: Future[Option[URI]] = currentFuture def withOverriddenEndpoint[R]( - future: Future[Option[Uri]] + future: Future[Option[URI]] )(action: => R): Unit = { val oldValue = currentFuture currentFuture = future diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/protocol/LoggingServiceEndpointSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/protocol/LoggingServiceEndpointSpec.scala index 14e3d2fd4c7c..006afcb20d3c 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/protocol/LoggingServiceEndpointSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/protocol/LoggingServiceEndpointSpec.scala @@ -1,11 +1,11 @@ package org.enso.projectmanager.protocol -import akka.http.scaladsl.model.Uri import io.circe.literal.JsonStringContext import org.enso.projectmanager.BaseServerSpec import org.enso.testkit.FlakySpec import scala.concurrent.Future +import java.net.URI class LoggingServiceEndpointSpec extends BaseServerSpec with FlakySpec { @@ -34,7 +34,7 @@ class LoggingServiceEndpointSpec extends BaseServerSpec with FlakySpec { "return the endpoint if it has been set-up" in { implicit val client = new WsTestClient(address) loggingService.withOverriddenEndpoint( - Future.successful(Some(Uri("ws://test-uri/"))) + Future.successful(Some(URI.create("ws://test-uri/"))) ) { client.send(json""" { "jsonrpc": "2.0", diff --git a/project/NativeImage.scala b/project/NativeImage.scala index 16131130b7a9..d4a64e31aee3 100644 --- a/project/NativeImage.scala +++ b/project/NativeImage.scala @@ -36,7 +36,8 @@ object NativeImage { "zio", "enumeratum", "akka", - "nl" + "nl", + "ch.qos.logback" ) /** Creates a task that builds a native image for the current project. From 0d55893579d15e04f8c0c04aa140379bdbd61112 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 14 Aug 2023 15:50:36 +0200 Subject: [PATCH 04/31] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3226c742b9bc..1f3b281a50b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -920,6 +920,7 @@ - [Using official BigInteger support][7420] - [Allow users to give a project other than Upper_Snake_Case name][7397] - [Support renaming variable or function][7515] +- [Replace custom logging service with off the shelf library][7559] - [Only use types as State keys][7585] - [Allow Java Enums in case of branches][7607] - [Notification about the project rename action][7613] @@ -1054,6 +1055,7 @@ [7420]: https://github.com/enso-org/enso/pull/7420 [7397]: https://github.com/enso-org/enso/pull/7397 [7515]: https://github.com/enso-org/enso/pull/7515 +[7559]: https://github.com/enso-org/enso/pull/7559 [7585]: https://github.com/enso-org/enso/pull/7585 [7607]: https://github.com/enso-org/enso/pull/7607 [7613]: https://github.com/enso-org/enso/pull/7613 From 51b87b31a822687eb6c3e404c571c9101f59d6c6 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 14 Aug 2023 15:50:47 +0200 Subject: [PATCH 05/31] minor formatting --- build.sbt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index 193bfd72a61f..e2ac08994362 100644 --- a/build.sbt +++ b/build.sbt @@ -920,9 +920,8 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) rebuildNativeImage := NativeImage .buildNativeImage( "project-manager", - staticOnLinux = true, - additionalOptions = Seq( - "-H:IncludeResources=.*logback.xml$"), + staticOnLinux = true, + additionalOptions = Seq("-H:IncludeResources=.*logback.xml$"), initializeAtRuntime = Seq( "scala.util.Random", "zio.internal.ZScheduler$$anon$4", @@ -1756,7 +1755,7 @@ lazy val `engine-runner` = project "sun.font", "java.awt", "com.sun.imageio", - "akka.http", + "akka.http" ) ) .dependsOn(installNativeImage) @@ -1800,7 +1799,7 @@ lazy val launcher = project "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", "-H:IncludeResources=.*Main.enso$", "-H:IncludeResources=.*logback.xml$" - ), + ) ) .dependsOn(installNativeImage) .dependsOn(assembly) From 407476fd8be501c2878dd581018aba0cec023d49 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 14 Aug 2023 17:06:25 +0200 Subject: [PATCH 06/31] fix test --- .../java/org/enso/interpreter/test/DebuggingEnsoTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java index b3cf7b917bd9..72a848f568ac 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java @@ -465,7 +465,7 @@ public void testSteppingOver() { """); List expectedLineNumbers = List.of(3, 4, 5); Queue steps = createStepOverEvents(expectedLineNumbers.size()); - testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); } /** @@ -498,7 +498,7 @@ public void testSteppingOverUseStdLib() { ); List expectedLineNumbers = mapLinesToLineNumbers(src, expectedLines); Queue steps = createStepOverEvents(expectedLineNumbers.size()); - testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); } @Test @@ -515,7 +515,7 @@ public void testSteppingInto() { Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)) ); - testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); } @Test @@ -531,7 +531,7 @@ public void testSteppingIntoMoreExpressionsOneLine() { Queue steps = new ArrayDeque<>( Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)) ); - testStepping(src, "foo", new Object[]{}, steps, expectedLineNumbers); + testStepping(src, "foo", new Object[]{0}, steps, expectedLineNumbers); } private static final class FrameEntry { From d597380426088535d20a219f9527f7ce17d256a5 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 14 Aug 2023 18:14:23 +0200 Subject: [PATCH 07/31] Bring back jul's formatting to runtime Conversion of raw messages to slf4j's style is now done in the handler. --- .../main/java/org/enso/compiler/Cache.java | 4 +-- .../interpreter/runtime/ThreadExecutors.java | 2 +- .../enso/compiler/SerializationManager.scala | 26 +++++++++---------- .../interpreter/test/DebuggingEnsoTest.java | 6 ++++- .../org/enso/logger/JavaLoggingForwarder.java | 11 ++++++-- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/compiler/Cache.java b/engine/runtime/src/main/java/org/enso/compiler/Cache.java index dec085d06cb5..210f710630c9 100644 --- a/engine/runtime/src/main/java/org/enso/compiler/Cache.java +++ b/engine/runtime/src/main/java/org/enso/compiler/Cache.java @@ -261,7 +261,7 @@ private Optional loadCacheFrom( if (cachedObject != null) { return Optional.of(cachedObject); } else { - logger.log(logLevel, "`{}` was corrupt on disk.", logName); + logger.log(logLevel, "`{0}` was corrupt on disk.", logName); invalidateCache(cacheRoot, logger); return Optional.empty(); } @@ -275,7 +275,7 @@ private Optional loadCacheFrom( return Optional.empty(); } } else { - logger.log(logLevel, "One or more digests did not match for the cache for [{}].", logName); + logger.log(logLevel, "One or more digests did not match for the cache for [{0}].", logName); invalidateCache(cacheRoot, logger); return Optional.empty(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java index 1f43357084e8..0f7aa3a63cc1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/ThreadExecutors.java @@ -42,7 +42,7 @@ public void shutdown() { success = false; } if (!success) { - context.getLogger().log(Level.WARNING, "Cannot shutdown {} thread pool", next.getValue()); + context.getLogger().log(Level.WARNING, "Cannot shutdown {0} thread pool", next.getValue()); } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala index b2a3054302ee..453ed7cee77c 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/SerializationManager.scala @@ -98,7 +98,7 @@ final class SerializationManager( ): Future[Boolean] = { compiler.context.logSerializationManager( debugLogLevel, - "Requesting serialization for module [{}].", + "Requesting serialization for module [{0}].", module.getName ) val duplicatedIr = compiler.updateMetadata( @@ -142,7 +142,7 @@ final class SerializationManager( ): Future[Boolean] = { compiler.context.logSerializationManager( Level.INFO, - "Requesting serialization for library [{}].", + "Requesting serialization for library [{0}].", libraryName ) @@ -179,7 +179,7 @@ final class SerializationManager( compiler.context.logSerializationManager( debugLogLevel, - "Running serialization for bindings [{}].", + "Running serialization for bindings [{0}].", libraryName ) startSerializing(libraryName.toQualifiedName) @@ -312,14 +312,14 @@ final class SerializationManager( case result @ Some(_: SuggestionsCache.CachedSuggestions) => compiler.context.logSerializationManager( Level.FINE, - "Restored suggestions for library [{}].", + "Restored suggestions for library [{0}].", libraryName ) result case _ => compiler.context.logSerializationManager( Level.FINEST, - "Unable to load suggestions for library [{}].", + "Unable to load suggestions for library [{0}].", libraryName ) None @@ -342,14 +342,14 @@ final class SerializationManager( case result @ Some(_: ImportExportCache.CachedBindings) => compiler.context.logSerializationManager( Level.FINE, - "Restored bindings for library [{}].", + "Restored bindings for library [{0}].", libraryName ) result case _ => compiler.context.logSerializationManager( Level.FINEST, - "Unable to load bindings for library [{}].", + "Unable to load bindings for library [{0}].", libraryName ) None @@ -396,7 +396,7 @@ final class SerializationManager( ) compiler.context.logSerializationManager( debugLogLevel, - "Restored IR from cache for module [{}] at stage [{}].", + "Restored IR from cache for module [{0}] at stage [{1}].", module.getName, loadedCache.compilationStage() ) @@ -405,14 +405,14 @@ final class SerializationManager( compiler.context.updateModule(module, _.hasCrossModuleLinks(true)) compiler.context.logSerializationManager( debugLogLevel, - "Restored links (early phase) in module [{}].", + "Restored links (early phase) in module [{0}].", module.getName ) Some(true) } else { compiler.context.logSerializationManager( debugLogLevel, - "Could not restore links (early phase) in module [{}].", + "Could not restore links (early phase) in module [{0}].", module.getName ) compiler.context.updateModule(module, _.hasCrossModuleLinks(false)) @@ -421,7 +421,7 @@ final class SerializationManager( case None => compiler.context.logSerializationManager( debugLogLevel, - "Unable to load a cache for module [{}].", + "Unable to load a cache for module [{0}].", module.getName ) None @@ -518,7 +518,7 @@ final class SerializationManager( val jobCount = waitingCount + isSerializing.size compiler.context.logSerializationManager( debugLogLevel, - "Waiting for #{} serialization jobs to complete.", + "Waiting for #{0} serialization jobs to complete.", jobCount ) @@ -585,7 +585,7 @@ final class SerializationManager( compiler.context.logSerializationManager( debugLogLevel, - "Running serialization for module [{}].", + "Running serialization for module [{0}].", name ) startSerializing(name) diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java index 72a848f568ac..2bb59d0aee92 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java @@ -55,7 +55,11 @@ public void initContext() { RuntimeOptions.LANGUAGE_HOME_OVERRIDE, Paths.get("../../distribution/component").toFile().getAbsolutePath() ) - .logHandler(OutputStream.nullOutputStream()) + .option( + RuntimeOptions.LOG_LEVEL, + "FINEST" + ) + .logHandler(System.out)//OutputStream.nullOutputStream()) .build(); context = Context.newBuilder() diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java index f5e752f199c6..baedc3d86428 100644 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java @@ -115,8 +115,15 @@ private ILoggingEvent toEvent(LogRecord record) { LoggingEvent event = new LoggingEvent(); event.setLoggerName(record.getLoggerName()); event.setLevel(toLogbackLevel(record.getLevel())); - event.setMessage(record.getMessage()); - event.setArgumentArray(record.getParameters()); + // Replace jul-specific placeholders with ones used by everybody else + event.setMessage(record.getMessage().replaceAll("\\{\\d+\\}", "{}")); + Object[] args; + if (record.getParameters().length == 1 && record.getParameters()[0] instanceof Object[]) { + args = (Object[]) (record.getParameters()[0]); + } else { + args = record.getParameters(); + } + event.setArgumentArray(args); event.setInstant(record.getInstant()); if (record.getThrown() != null) { event.setThrowableProxy(new ThrowableProxy(record.getThrown())); From 7b0afd4dc8f906bb9b2047dd6279f8c9f6c4f41f Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 15 Aug 2023 18:08:58 +0200 Subject: [PATCH 08/31] Fix some tests Still not behaving as expected when setting up loggers for launcher. --- .../src/main/resources/application.conf | 2 +- ...auncher-logback.xml => launcher.logback.xml} | 11 +++++++++-- .../enso/launcher/cli/GlobalCLIOptions.scala | 7 ++++--- .../enso/launcher/cli/LauncherApplication.scala | 13 ++++++------- .../main/scala/org/enso/launcher/cli/Main.scala | 2 +- .../components/LauncherRunnerSpec.scala | 1 - .../enso/runner/DependencyPreinstaller.scala | 6 ++---- .../org/enso/interpreter/epb/EpbContext.java | 2 +- .../main/scala/org/enso/compiler/Compiler.scala | 3 ++- .../org/enso/logger/LoggerContextSetup.java | 17 ++++++++++++----- .../org/enso/logger/config/LoggingService.java | 12 ++++++++++++ .../main/resources/logging-server.logback.xml | 2 ++ .../enso/logging/LoggingCollectorHelper.scala | 16 ++++++++++++++-- .../test/scala/org/enso/logger/TestLogger.scala | 3 --- .../runtimeversionmanager/cli/Arguments.scala | 15 ++++++++++++++- 15 files changed, 80 insertions(+), 32 deletions(-) rename engine/launcher/src/main/resources/{launcher-logback.xml => launcher.logback.xml} (71%) diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index eac6d995eb91..293afb9210e1 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -11,7 +11,7 @@ logging-service { akka.io = error akka.stream = error } - appender = "forward-via-socket" + appender = "console" #"forward-via-socket" server { hostname = "localhost" port = 6000 diff --git a/engine/launcher/src/main/resources/launcher-logback.xml b/engine/launcher/src/main/resources/launcher.logback.xml similarity index 71% rename from engine/launcher/src/main/resources/launcher-logback.xml rename to engine/launcher/src/main/resources/launcher.logback.xml index 5c5dbc1ef2a0..c83d8ec4956c 100644 --- a/engine/launcher/src/main/resources/launcher-logback.xml +++ b/engine/launcher/src/main/resources/launcher.logback.xml @@ -2,7 +2,6 @@ - $launcher.logRoot}/${launcher.logPrefix}-%d{yyyy-MM-dd}.log false @@ -12,6 +11,12 @@ + + + [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n + + + ${logging-server.host} ${logging-server.port} @@ -19,7 +24,9 @@ true + + - + diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala index 0df1077cda56..de92e2c50931 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala @@ -1,11 +1,12 @@ package org.enso.launcher.cli -import akka.http.scaladsl.model.Uri import org.enso.cli.arguments.{Argument, OptsParseError} import org.enso.launcher.cli.GlobalCLIOptions.InternalOptions import org.enso.logger.ColorMode.{Always, Auto, Never} import org.enso.logger.ColorMode +import java.net.URI + //import org.enso.loggingservice.ColorMode.{Always, Auto, Never} //import org.enso.loggingservice.{ColorMode, LogLevel} import org.slf4j.event.Level @@ -27,7 +28,7 @@ case class GlobalCLIOptions( autoConfirm: Boolean, hideProgress: Boolean, useJSON: Boolean, - colorMode: ColorMode, + colorMode: ColorMode, // TODO: no longer supported internalOptions: InternalOptions ) @@ -42,7 +43,7 @@ object GlobalCLIOptions { */ case class InternalOptions( launcherLogLevel: Option[Level], - loggerConnectUri: Option[Uri], + loggerConnectUri: Option[URI], logMaskingDisabled: Boolean ) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index 7f9ba36f8a6b..c0941c62117a 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -1,6 +1,5 @@ package org.enso.launcher.cli -import akka.http.scaladsl.model.Uri import cats.data.NonEmptyList import cats.implicits._ import nl.gn0s1s.bump.SemVer @@ -16,13 +15,13 @@ import org.enso.launcher.installation.DistributionInstaller.BundleAction import org.enso.launcher.upgrade.LauncherUpgrader import org.enso.launcher.{cli, Launcher} import org.enso.logger.ColorMode -//import org.enso.loggingservice.{ColorMode, LogLevel} import org.enso.runtimeversionmanager.cli.Arguments._ import org.enso.runtimeversionmanager.runner.LanguageServerOptions import org.slf4j.event.Level import java.nio.file.Path import java.util.UUID +import java.net.URI /** Defines the CLI commands and options for the program. * @@ -611,11 +610,11 @@ object LauncherApplication { val logLevel = Opts.optionalParameter[Level]( GlobalCLIOptions.LOG_LEVEL, "(error | warning | info | debug | trace)", - "Sets logging verbosity for the launcher. If not provided, defaults to" + + "Sets logging verbosity for the launcher. If not provided, defaults to " + s"${LauncherLogging.defaultLogLevel}." ) val connectLogger = Opts - .optionalParameter[Uri]( + .optionalParameter[URI]( GlobalCLIOptions.CONNECT_LOGGER, "URI", "Instead of starting its own logging service, " + @@ -684,13 +683,13 @@ object LauncherApplication { internalOptsCallback(globalCLIOptions) LauncherUpgrader.setCLIOptions(globalCLIOptions) - /*LauncherLogging.setup( + LauncherLogging.setup( logLevel, connectLogger, - globalCLIOptions.colorMode, !disableLogMasking, None - )*/ + ) + LauncherLogging.waitForSetup() initializeApp() if (version) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index a5af5af20f46..7bc79fc7a806 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -55,7 +55,7 @@ object Main { * @param exitCode exit code to return */ def exit(exitCode: Int): Nothing = { - //LauncherLogging.tearDown() + LauncherLogging.tearDown() DefaultManagers.defaultResourceManager.releaseMainLock() sys.exit(exitCode) } diff --git a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala index 3097e33fa494..5745cc2ee7e3 100644 --- a/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala +++ b/engine/launcher/src/test/scala/org/enso/launcher/components/LauncherRunnerSpec.scala @@ -196,7 +196,6 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest with FlakySpec { .get } ) - println("ALL LOGS: " + logs.size) assert( logs.exists(msg => msg.level == Level.WARN && msg.msg.contains( diff --git a/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala b/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala index a3d794747a16..488a79ab5ba0 100644 --- a/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala +++ b/engine/runner/src/main/scala/org/enso/runner/DependencyPreinstaller.scala @@ -1,9 +1,8 @@ package org.enso.runner import cats.implicits.toTraverseOps -import org.slf4j.LoggerFactory import org.slf4j.event.Level -//import com.typesafe.scalalogging.Logger +import com.typesafe.scalalogging.Logger import org.enso.cli.ProgressBar import org.enso.cli.task.{ProgressReporter, TaskProgress} import org.enso.distribution.locking.{ @@ -18,7 +17,6 @@ import org.enso.editions.{DefaultEdition, EditionResolver} import org.enso.languageserver.libraries.CompilerBasedDependencyExtractor import org.enso.librarymanager.dependencies.DependencyResolver import org.enso.librarymanager.{DefaultLibraryProvider, LibraryResolver} -//import org.enso.loggingservice.LogLevel import org.enso.pkg.PackageManager import java.io.File @@ -31,7 +29,7 @@ object DependencyPreinstaller { * installed. */ def preinstallDependencies(projectRoot: File, logLevel: Level): Unit = { - val logger = LoggerFactory.getLogger(classOf[DependencyPreinstaller.type]) + val logger = Logger[DependencyPreinstaller.type] val pkg = PackageManager.Default.loadPackage(projectRoot).get val dependencyExtractor = new CompilerBasedDependencyExtractor(logLevel) diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java index a1f4814db76b..1a4bb073613c 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java @@ -63,7 +63,7 @@ private static void initializeLanguages( return; } var log = environment.getLogger(EpbContext.class); - log.log(Level.FINE, "Initializing languages {}", langs); + log.log(Level.FINE, "Initializing languages {0}", langs); var cdl = new CountDownLatch(1); var run = (Consumer) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index e22970720951..0ddb2db39f12 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -200,7 +200,8 @@ class Compiler( context.log( Level.SEVERE, "Could not find entry point for compilation in package [{0}.{1}]", - Array(pkg.namespace, pkg.normalizedName) + pkg.namespace, + pkg.normalizedName ) CompletableFuture.completedFuture(false) case Some(m) => diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java index b10d5f08dc24..9b5fe0d8585f 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java @@ -51,7 +51,9 @@ public static Boolean setup( var context = (LoggerContext) LoggerFactory.getILoggerFactory(); var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); - System.setProperty(componentName + ".appender", appenderName); + if (appenderName != null) { + System.setProperty(componentName + ".appender", appenderName); + } if (hostname != null) { System.setProperty("logging-server.host", hostname); } @@ -67,15 +69,20 @@ public static Boolean setup( configurator.doConfigure(customLogConfig); var rootLogger = context.getLogger("root"); var appender = rootLogger.getAppender(appenderName); - if (appender == null) { - System.err.println( - "Failed to apply custom log levels for application loggers' in " + appenderName); - } else { + if (appender != null) { if (loggers != null) { var filter = ApplicationFilter.fromLoggers(loggers); appender.addFilter(filter); } } + /* + // TODO: report if cannot customize the appender + else if (appenderName != null) { + //System.err.println( + // "Failed to apply custom log levels for application loggers' in " + appenderName + " for " + componentName); + } + */ + return true; } catch (JoranException je) { je.printStackTrace(); diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java index 8745b07a5c49..77559a9b82f0 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java @@ -57,4 +57,16 @@ public Optional getTestLogLevel() { public Server getServer() { return server; } + + @Override + public String toString() { + return "Loggers: " + + loggers + + ", appender: " + + appender + + ", testLogLevel: " + + testLogLevel.orElseGet(() -> "unknown") + + ", server: " + + server; + } } diff --git a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml index 9f85429d1771..5e66d871fc0d 100644 --- a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml +++ b/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml @@ -20,6 +20,8 @@ + + diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala index 201e477e75a0..4735c143d9fa 100644 --- a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala +++ b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala @@ -49,6 +49,13 @@ abstract class LoggingCollectorHelper(implicit @unused profilingLog: Option[Path] ): Unit = { val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + logConfiguration() + .map(conf => + Boolean.unbox( + LoggerContextSetup + .setup(actualLogLevel, logComponentName, conf, null, null, 0, null) + ) + ) val loggingService = LoggingService.parseConfig() //TODO: Masking.setup(logMasking) val (host, port, serverNeedsInitialization) = @@ -101,7 +108,7 @@ abstract class LoggingCollectorHelper(implicit ) .getOrElse(false) if (!result) { - System.err.println("Failed to set Logger Context") + System.err.println("Failed to set Logger Context1") } loggingServiceEndpointPromise.success(Some(uri)) } @@ -122,12 +129,17 @@ abstract class LoggingCollectorHelper(implicit ) .getOrElse(false) if (!result) { - System.err.println("Failed to set Logger Context") + System.err.println("Failed to set Logger Context2") } } + Thread.sleep(1000) } def waitForSetup(): Unit = { Await.ready(loggingServiceEndpointPromise.future, 5.seconds) } + + def tearDown(): Unit = { + LoggingServiceManager.teardown() + } } diff --git a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala index 306d1b21b903..975ebc66dc1c 100644 --- a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala +++ b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala @@ -16,9 +16,6 @@ object TestLogger { logger.addAppender(appender) appender.start() - println( - "Found logs; " + appender.size() + " appender: " + appender.isStarted() - ) val result = action (result, appender.allEvents()) } diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala index cfda25d2e9a9..ee2119956368 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala @@ -3,11 +3,14 @@ package org.enso.runtimeversionmanager.cli import akka.http.scaladsl.model.{IllegalUriException, Uri} import org.enso.cli.arguments.{Argument, OptsParseError} import org.enso.logger.Converter + +import java.net.URI +import java.net.URISyntaxException //import org.enso.loggingservice.LogLevel import org.slf4j.event.Level object Arguments { - implicit val uriArgument: Argument[Uri] = (string: String) => + implicit val uriAkkaArgument: Argument[Uri] = (string: String) => try { Right(Uri(string)) } catch { @@ -15,6 +18,16 @@ object Arguments { Left(OptsParseError(s"`$string` is not a valid Uri: $error.")) } + implicit val uriArgument: Argument[URI] = (string: String) => + try { + Right(URI.create(string)) + } catch { + case error: IllegalArgumentException => + Left(OptsParseError(s"`$string` is not a valid URI: $error.")) + case error: URISyntaxException => + Left(OptsParseError(s"`$string` is not a valid URI: $error.")) + } + implicit val logLevelArgument: Argument[Level] = (string: String) => { val provided = Converter.backwardCompatibleName(string.toLowerCase) Level From 372af9e975e1fc3c32b61f6b4a75ca1b21b036d7 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 15 Aug 2023 22:37:41 +0200 Subject: [PATCH 09/31] Use test logger when collecting logs of a specific test --- .../enso/launcher/cli/LauncherLogging.scala | 8 +-- .../DistributionUninstaller.scala | 2 +- .../repository/LibraryDownloadTest.scala | 55 +++++++++---------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index c67ed609da8c..c7f7e2c494a8 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -34,11 +34,9 @@ object LauncherLogging extends LoggingCollectorHelper { * This is necessary on Windows to ensure that the logs file is closed, so * that the log directory can be removed. */ - def prepareForUninstall(): Unit = { //colorMode: ColorMode): Unit = { - // TODO + def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() - - // TODO: fetch actual log level - LoggingServiceManager.fallbackToLocalConsole(defaultLogLevel, "launcher") + val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + LoggingServiceManager.fallbackToLocalConsole(actualLogLevel, "launcher") } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala index d4a2bd4902f2..0f27c894bda8 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala @@ -199,7 +199,7 @@ class DistributionUninstaller( dataRoot.toAbsolutePath.normalize ) if (logsInsideData) { - LauncherLogging.prepareForUninstall() //globalCLIOptions.colorMode) + LauncherLogging.prepareForUninstall(globalCLIOptions.internalOptions.launcherLogLevel) //globalCLIOptions.colorMode) } for (dirName <- knownDataDirectories) { diff --git a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala index 0dca502de4ce..59aacb020ebe 100644 --- a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala +++ b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala @@ -2,14 +2,13 @@ package org.enso.librarymanager.published.repository import org.enso.editions.Editions import org.enso.librarymanager.published.cache.DownloadingLibraryCache -import org.enso.logger.{TestAppender, TestLogMessage} +import org.enso.logger.TestLogMessage import org.enso.pkg.PackageManager import org.enso.testkit.WithTemporaryDirectory import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -import org.slf4j.LoggerFactory import org.slf4j.event.Level -import ch.qos.logback.classic.Logger +import org.enso.logger.TestLogger import java.nio.file.Files @@ -34,33 +33,29 @@ class LibraryDownloadTest repo.testLib.version ) shouldBe empty - val logger = LoggerFactory - .getLogger(classOf[DownloadingLibraryCache]) - .asInstanceOf[Logger] - val appender = new TestAppender() - logger.addAppender(appender) - //logger.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); - val libPath = - cache - .findOrInstallLibrary( - repo.testLib.libraryName, - repo.testLib.version, - Editions - .Repository("test_repo", s"http://localhost:$port/libraries") - ) - .get - val pkg = - PackageManager.Default.loadPackage(libPath.location.toFile).get - pkg.normalizedName shouldEqual "Bar" - val sources = pkg.listSources() - sources should have size 1 - sources.head.file.getName shouldEqual "Main.enso" - assert( - Files.notExists(libPath / "LICENSE.md"), - "The license file should not exist as it was not provided " + - "in the repository." - ) - appender.allEvents() should contain( + val (_, allLogs) = TestLogger.gather[Any, DownloadingLibraryCache](classOf[DownloadingLibraryCache], { + val libPath = + cache + .findOrInstallLibrary( + repo.testLib.libraryName, + repo.testLib.version, + Editions + .Repository("test_repo", s"http://localhost:$port/libraries") + ) + .get + val pkg = + PackageManager.Default.loadPackage(libPath.location.toFile).get + pkg.normalizedName shouldEqual "Bar" + val sources = pkg.listSources() + sources should have size 1 + sources.head.file.getName shouldEqual "Main.enso" + assert( + Files.notExists(libPath / "LICENSE.md"), + "The license file should not exist as it was not provided " + + "in the repository." + ) + }) + allLogs should contain( TestLogMessage( Level.WARN, "License file for library [Foo.Bar:1.0.0] was missing." From f03d2637f3950611eb0c4e6fcc17d8b00f604b73 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 15 Aug 2023 23:38:54 +0200 Subject: [PATCH 10/31] Bandaid to make sure launcher's logging works When started, launcher should already make use of the logging's configuration. --- .../scala/org/enso/launcher/cli/Main.scala | 7 ++- .../DistributionUninstaller.scala | 4 +- .../repository/LibraryDownloadTest.scala | 47 ++++++++++--------- .../org/enso/logger/LoggerContextSetup.java | 14 +++++- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index 7bc79fc7a806..73d59e910803 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -1,6 +1,7 @@ package org.enso.launcher.cli import com.typesafe.scalalogging.Logger +import org.enso.logger.LoggerContextSetup import org.enso.cli.CLIOutput import org.enso.launcher.distribution.DefaultManagers import org.enso.launcher.upgrade.LauncherUpgrader @@ -8,11 +9,13 @@ import org.enso.launcher.upgrade.LauncherUpgrader /** Defines the entry point for the launcher. */ object Main { - private def setup(): Unit = + private def setup(): Unit = { System.setProperty( "org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog" ) + LoggerContextSetup.setup("launcher") + } private def runAppHandlingParseErrors(args: Array[String]): Int = LauncherApplication.application.run(args) match { @@ -46,7 +49,7 @@ object Main { /** Exits the program in a safe way. * - * This should be used ofer `sys.exit` to ensure that all services are + * This should be used after `sys.exit` to ensure that all services are * terminated gracefully and locks are released quickly (as the OS cleanup * may take a longer while). The only exception is for functions in the * [[InternalOpts]], because they may need to terminate the program as diff --git a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala index 0f27c894bda8..fe1c0eca01fe 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/installation/DistributionUninstaller.scala @@ -199,7 +199,9 @@ class DistributionUninstaller( dataRoot.toAbsolutePath.normalize ) if (logsInsideData) { - LauncherLogging.prepareForUninstall(globalCLIOptions.internalOptions.launcherLogLevel) //globalCLIOptions.colorMode) + LauncherLogging.prepareForUninstall( + globalCLIOptions.internalOptions.launcherLogLevel + ) } for (dirName <- knownDataDirectories) { diff --git a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala index 59aacb020ebe..e9ac9658ba56 100644 --- a/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala +++ b/lib/scala/library-manager-test/src/test/scala/org/enso/librarymanager/published/repository/LibraryDownloadTest.scala @@ -33,28 +33,33 @@ class LibraryDownloadTest repo.testLib.version ) shouldBe empty - val (_, allLogs) = TestLogger.gather[Any, DownloadingLibraryCache](classOf[DownloadingLibraryCache], { - val libPath = - cache - .findOrInstallLibrary( - repo.testLib.libraryName, - repo.testLib.version, - Editions - .Repository("test_repo", s"http://localhost:$port/libraries") - ) - .get - val pkg = - PackageManager.Default.loadPackage(libPath.location.toFile).get - pkg.normalizedName shouldEqual "Bar" - val sources = pkg.listSources() - sources should have size 1 - sources.head.file.getName shouldEqual "Main.enso" - assert( - Files.notExists(libPath / "LICENSE.md"), - "The license file should not exist as it was not provided " + + val (_, allLogs) = TestLogger.gather[Any, DownloadingLibraryCache]( + classOf[DownloadingLibraryCache], { + val libPath = + cache + .findOrInstallLibrary( + repo.testLib.libraryName, + repo.testLib.version, + Editions + .Repository( + "test_repo", + s"http://localhost:$port/libraries" + ) + ) + .get + val pkg = + PackageManager.Default.loadPackage(libPath.location.toFile).get + pkg.normalizedName shouldEqual "Bar" + val sources = pkg.listSources() + sources should have size 1 + sources.head.file.getName shouldEqual "Main.enso" + assert( + Files.notExists(libPath / "LICENSE.md"), + "The license file should not exist as it was not provided " + "in the repository." - ) - }) + ) + } + ) allLogs should contain( TestLogMessage( Level.WARN, diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java index 9b5fe0d8585f..521c30c7f691 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java @@ -12,6 +12,14 @@ public class LoggerContextSetup { + public static Boolean setup(String componentName) { + var resource = + LoggerContextSetup.class.getResourceAsStream("/" + componentName + ".logback.xml"); + return resource != null + ? setup(null, componentName, resource, LoggingService.parseConfig()) + : false; + } + public static Boolean setup(Level level, String componentName, InputStream customLogConfig) { return setup(level, componentName, customLogConfig, LoggingService.parseConfig()); } @@ -49,8 +57,10 @@ public static Boolean setup( int port, Loggers loggers) { var context = (LoggerContext) LoggerFactory.getILoggerFactory(); - var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); - System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); + if (logLevel != null) { + var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); + System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); + } if (appenderName != null) { System.setProperty(componentName + ".appender", appenderName); } From 80f0f12b3a2995cec0d7d3eb8c1ea3c5276ae7cd Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 21 Aug 2023 00:23:26 +0200 Subject: [PATCH 11/31] Significantly simplifying logger's setup --- build.sbt | 49 ++--- .../src/main/resources/application.conf | 21 +- .../resources/language-server.logback.xml | 26 --- .../boot/LanguageServerComponent.scala | 14 -- .../enso/languageserver/boot/MainModule.scala | 4 +- .../CompilerBasedDependencyExtractor.scala | 4 +- .../src/test/resources/application.conf | 16 +- .../src/test/resources/logback-test.xml | 24 --- .../websocket/json/BaseServerTest.scala | 4 + .../src/main/resources/application.conf | 26 ++- .../src/main/resources/launcher.logback.xml | 32 --- .../enso/launcher/cli/GlobalCLIOptions.scala | 44 +---- .../launcher/cli/LauncherApplication.scala | 22 +-- .../enso/launcher/cli/LauncherLogging.scala | 8 +- .../scala/org/enso/launcher/cli/Main.scala | 12 +- .../src/test/resources/application.conf | 11 ++ .../src/main/resources/runner.logback.xml | 21 -- .../org/enso/runner/ContextFactory.scala | 5 +- .../scala/org/enso/runner/RunnerLogging.scala | 37 ++-- .../runtime/DefaultPackageRepository.scala | 2 +- .../config/GlobalConfigurationManager.scala | 4 +- .../java/org/enso/logger/config/Appender.java | 32 +++ .../enso/logger/config/ConsoleAppender.java | 27 +++ .../org/enso/logger/config/FileAppender.java | 40 ++++ .../java/org/enso/logger/config/Loggers.java | 31 ++- .../logger/config/LoggingServiceConfig.java | 116 +++++++++++ .../enso/logger/config/SentryAppender.java | 23 +++ .../java/org/enso/logger/config/Server.java | 15 ++ .../enso/logger/config/SocketAppender.java | 43 ++++ .../org/enso/logger/JavaLoggingForwarder.java | 169 ---------------- .../main/java/org/enso/logger/JulHandler.java | 62 ++++++ .../org/enso/logger/ApplicationFilter.java | 4 +- .../org/enso/logger/LoggerContextSetup.java | 109 ----------- .../java/org/enso/logger/LoggerSetup.java | 183 ++++++++++++++++++ .../enso/logger/config/LoggingService.java | 72 ------- .../java/org/enso/logger/config/Server.java | 12 -- .../org/enso/logging/ForwardToServer.java | 21 +- .../logging/LoggerInitializationFailed.java | 7 + .../java/org/enso/logging/LoggingService.java | 0 .../main/resources/logging-server.logback.xml | 4 +- .../enso/logging/LoggingServiceManager.scala | 30 +-- .../org/enso/logging/LoggingSetupHelper.scala | 144 ++++++++++++++ .../java/org/enso/logging/ExternalLogger.java | 28 --- .../src/main/java/org/enso/logging/Local.java | 23 --- .../enso/logging/LoggingCollectorHelper.scala | 145 -------------- .../scala/org/enso/logger/ColorMode.scala | 20 -- .../src/main/resources/application.conf | 30 ++- .../resources/project-manager.logback.xml | 24 --- .../enso/projectmanager/boot/Logging.scala | 7 +- .../projectmanager/boot/ProjectManager.scala | 3 +- .../src/test/resources/application.conf | 15 +- .../enso/projectmanager/BaseServerSpec.scala | 17 +- .../LanguageServerSupervisorSpec.scala | 8 +- .../testing/FakeReleaseProvider.scala | 23 ++- 54 files changed, 932 insertions(+), 941 deletions(-) delete mode 100644 engine/language-server/src/main/resources/language-server.logback.xml delete mode 100644 engine/language-server/src/test/resources/logback-test.xml delete mode 100644 engine/launcher/src/main/resources/launcher.logback.xml delete mode 100644 engine/runner/src/main/resources/runner.logback.xml create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java rename lib/scala/{logging-logback => logging-config}/src/main/java/org/enso/logger/config/Loggers.java (55%) create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java delete mode 100644 lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java create mode 100644 lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java delete mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java delete mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java delete mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java rename lib/scala/{logging-socket-collector => logging-server}/src/main/java/org/enso/logging/ForwardToServer.java (56%) create mode 100644 lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java rename lib/scala/{logging-socket-collector => logging-server}/src/main/java/org/enso/logging/LoggingService.java (100%) rename lib/scala/{logging-socket-collector => logging-server}/src/main/resources/logging-server.logback.xml (90%) rename lib/scala/{logging-socket-collector => logging-server}/src/main/scala/org/enso/logging/LoggingServiceManager.scala (60%) create mode 100644 lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala delete mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java delete mode 100644 lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java delete mode 100644 lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala delete mode 100644 lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala delete mode 100644 lib/scala/project-manager/src/main/resources/project-manager.logback.xml diff --git a/build.sbt b/build.sbt index e2ac08994362..a04fe6591c37 100644 --- a/build.sbt +++ b/build.sbt @@ -267,8 +267,9 @@ lazy val enso = (project in file(".")) `profiling-utils`, `logging-utils`, `logging-jutil`, + `logging-config`, `logging-logback`, - `logging-socket-collector`, + `logging-server`, `logging-utils-akka`, filewatcher, `logging-service`, @@ -689,8 +690,8 @@ lazy val `logging-utils` = project ) ) -lazy val `logging-socket-collector` = project - .in(file("lib/scala/logging-socket-collector")) +lazy val `logging-server` = project + .in(file("lib/scala/logging-server")) .configs(Test) .settings( frgaalJavaCompilerSetting, @@ -699,7 +700,6 @@ lazy val `logging-socket-collector` = project "org.slf4j" % "slf4j-api" % slf4jVersion, "ch.qos.logback" % "logback-classic" % logbackClassicVersion, "ch.qos.logback" % "logback-core" % logbackClassicVersion, - "com.typesafe" % "config" % typesafeConfigVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp ) @@ -713,16 +713,23 @@ lazy val `logging-jutil` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, - akkaHttp + "org.slf4j" % "slf4j-api" % slf4jVersion ) ) .dependsOn(`logging-logback`) +lazy val `logging-config` = project + .in(file("lib/scala/logging-config")) + .configs(Test) + .settings( + frgaalJavaCompilerSetting, + version := "0.1", + libraryDependencies ++= Seq( + "com.typesafe" % "config" % typesafeConfigVersion, + "org.slf4j" % "slf4j-api" % slf4jVersion + ) + ) + lazy val `logging-logback` = project .in(file("lib/scala/logging-logback")) .configs(Test) @@ -737,6 +744,7 @@ lazy val `logging-logback` = project akkaHttp ) ) + .dependsOn(`logging-config`) lazy val `logging-utils-akka` = project .in(file("lib/scala/logging-utils-akka")) @@ -746,8 +754,7 @@ lazy val `logging-utils-akka` = project version := "0.1", libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, - "com.typesafe.akka" %% "akka-actor" % akkaVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test + "com.typesafe.akka" %% "akka-actor" % akkaVersion ) ) @@ -950,7 +957,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .dependsOn(`runtime-version-manager`) .dependsOn(`library-manager`) .dependsOn(`logging-utils-akka`) - .dependsOn(`logging-socket-collector`) + .dependsOn(`logging-server`) .dependsOn(pkg) .dependsOn(`json-rpc-server`) .dependsOn(`json-rpc-server-test` % Test) @@ -1210,9 +1217,8 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`library-manager`) .dependsOn(`connected-lock-manager`) .dependsOn(`edition-updater`) - //.dependsOn(`logging-service`) .dependsOn(`logging-utils-akka`) - .dependsOn(`logging-socket-collector`) + .dependsOn(`logging-server`) .dependsOn(`logging-jutil`) .dependsOn(`polyglot-api`) .dependsOn(`searcher`) @@ -1484,7 +1490,6 @@ lazy val runtime = (project in file("engine/runtime")) .dependsOn(`interpreter-dsl`) .dependsOn(`library-manager`) .dependsOn(`logging-truffle-connector`) - .dependsOn(`logging-utils`) .dependsOn(`polyglot-api`) .dependsOn(`text-buffer`) .dependsOn(`runtime-parser`) @@ -1735,7 +1740,6 @@ lazy val `engine-runner` = project additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", "-H:IncludeResources=.*Main.enso$", - "-H:IncludeResources=.*logback.xml$", "--macro:truffle", "--language:js", // "-g", @@ -1797,8 +1801,7 @@ lazy val launcher = project staticOnLinux = true, additionalOptions = Seq( "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", - "-H:IncludeResources=.*Main.enso$", - "-H:IncludeResources=.*logback.xml$" + "-H:IncludeResources=.*Main.enso$" ) ) .dependsOn(installNativeImage) @@ -1831,14 +1834,17 @@ lazy val launcher = project .dependsOn(buildNativeImage) .dependsOn(LauncherShimsForTest.prepare()) .value, - Test / parallelExecution := false + (Test / testOnly) := (Test / testOnly) + .dependsOn(buildNativeImage) + .dependsOn(LauncherShimsForTest.prepare()) + .evaluated ) .dependsOn(cli) .dependsOn(`runtime-version-manager`) .dependsOn(`version-output`) .dependsOn(pkg) .dependsOn(`logging-utils` % "test->test") - .dependsOn(`logging-socket-collector`) + .dependsOn(`logging-server`) .dependsOn(`distribution-manager` % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -2067,7 +2073,6 @@ lazy val `library-manager` = project .dependsOn(`distribution-manager`) .dependsOn(downloader) .dependsOn(testkit % Test) -//.dependsOn(`logging-service` % Test) lazy val `library-manager-test` = project .in(file("lib/scala/library-manager-test")) diff --git a/engine/language-server/src/main/resources/application.conf b/engine/language-server/src/main/resources/application.conf index c8b75e4839e0..a41cd38370d7 100644 --- a/engine/language-server/src/main/resources/application.conf +++ b/engine/language-server/src/main/resources/application.conf @@ -1,3 +1,5 @@ +## Language Server's application.conf + akka { actor.debug.lifecycle = on http { @@ -26,9 +28,18 @@ logging-service { org.enso.jsonrpc.JsonRpcServer = debug # verbose in trace mode org.enso.languageserver.runtime.RuntimeConnector = debug } - appender = "forward-via-socket" - server { - hostname = "localhost" - port = 6000 - } + appenders = [ + { + name = "socket" + hostname = ${?LOGSERVER_HOSTNAME} + hostname = "localhost" + port = ${?LOGSERVER_PORT} + port = 6000 + }, + { + name = "console" + } + ] + defaultAppender = ${?DEFAULT_LOGGER} + defaultAppender = socket } diff --git a/engine/language-server/src/main/resources/language-server.logback.xml b/engine/language-server/src/main/resources/language-server.logback.xml deleted file mode 100644 index 82b757800da8..000000000000 --- a/engine/language-server/src/main/resources/language-server.logback.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - ${logging-server.host} - ${logging-server.port} - 10000 - true - - - - - - - diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala index 74b15467d85d..5b212f81507e 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala @@ -14,11 +14,8 @@ import org.enso.languageserver.runtime.RuntimeKiller.{ RuntimeShutdownResult, ShutDownRuntime } - import org.enso.profiling.{FileSampler, MethodsSampler, NoopSampler} import org.slf4j.event.Level -import org.enso.logger.LoggerContextSetup - import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContextExecutor, Future} @@ -39,7 +36,6 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) /** @inheritdoc */ override def start(): Future[ComponentStarted.type] = { - setupLogging() logger.info("Starting Language Server...") val sampler = startSampling(config) val module = new MainModule(config, logLevel) @@ -69,16 +65,6 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) } yield ComponentStarted } - private def setupLogging(): Unit = { - val logConfig = - this.getClass.getResourceAsStream("/language-server.logback.xml") - if (logConfig != null) { - LoggerContextSetup.setup(logLevel, "language-server", logConfig); - } else { - System.err.println("Unable to set up logging for Language Server.") - } - } - /** Start the application sampling. */ private def startSampling(config: LanguageServerConfig): MethodsSampler = { val sampler = config.profilingConfig.profilingPath match { 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 6b3114b6cc2c..58da2fc64b2b 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 @@ -45,7 +45,7 @@ import org.enso.librarymanager.published.PublishedLibraryCache import org.enso.lockmanager.server.LockManagerService import org.enso.logger.Converter import org.enso.logger.masking.{MaskedPath, Masking} -import org.enso.logger.JavaLoggingForwarder +import org.enso.logger.JulHandler import org.enso.logger.akka.AkkaConverter import org.enso.polyglot.{HostAccessFactory, RuntimeOptions, RuntimeServerInfo} import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo} @@ -309,7 +309,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { .out(stdOut) .err(stdErr) .in(stdIn) - .logHandler(new JavaLoggingForwarder()) + .logHandler(new JulHandler()) .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/libraries/CompilerBasedDependencyExtractor.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala index 4a6cf465edd9..ef750b1e91d3 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala @@ -4,7 +4,7 @@ import org.enso.editions.LibraryName import org.enso.libraryupload.DependencyExtractor import org.enso.logger.Converter -import org.enso.logger.JavaLoggingForwarder +import org.enso.logger.JulHandler import org.enso.pkg.Package import org.enso.pkg.SourceFile import org.enso.polyglot.{HostAccessFactory, PolyglotContext, RuntimeOptions} @@ -65,7 +65,7 @@ class CompilerBasedDependencyExtractor(logLevel: Level) RuntimeOptions.LOG_LEVEL, Converter.toJavaLevel(logLevel).getName ) - .logHandler(new JavaLoggingForwarder()) + .logHandler(new JulHandler()) .build new PolyglotContext(context) } diff --git a/engine/language-server/src/test/resources/application.conf b/engine/language-server/src/test/resources/application.conf index 6160d14db9ee..58afffe0b834 100644 --- a/engine/language-server/src/test/resources/application.conf +++ b/engine/language-server/src/test/resources/application.conf @@ -20,13 +20,13 @@ logging-service { slick."*" = error org.eclipse.jgit = error io.methvin.watcher = error - org.enso.languageserver.protocol.json.JsonConnectionController = debug # very verbose in trace mode - org.enso.jsonrpc.JsonRpcServer = debug # verbose in trace mode - org.enso.languageserver.runtime.RuntimeConnector = debug - } - appender = "forward-via-socket" - server { - hostname = "localhost" - port = 6000 } + appenders = [ + { + name = "console" + pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" + } + ] + defaultAppender = console + log-level = "error" } diff --git a/engine/language-server/src/test/resources/logback-test.xml b/engine/language-server/src/test/resources/logback-test.xml deleted file mode 100644 index 3237c2767743..000000000000 --- a/engine/language-server/src/test/resources/logback-test.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n - - --> - - - - - - - - - - - - - - diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala index b3852b29a779..e9b4842eb631 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala @@ -64,6 +64,8 @@ import java.util.UUID import scala.concurrent.Await import scala.concurrent.duration._ +import org.enso.logger.LoggerSetup + class BaseServerTest extends JsonRpcServerTestKit with EitherValue @@ -75,6 +77,8 @@ class BaseServerTest val timeout: FiniteDuration = 10.seconds + LoggerSetup.setup() + def isFileWatcherEnabled: Boolean = false val testContentRootId = UUID.randomUUID() diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index 293afb9210e1..813ab53bce3d 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -1,3 +1,5 @@ +## Launcher's application.conf + akka { loggers = ["akka.event.slf4j.Slf4jLogger"] logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" @@ -11,9 +13,23 @@ logging-service { akka.io = error akka.stream = error } - appender = "console" #"forward-via-socket" - server { - hostname = "localhost" - port = 6000 - } + appenders = [ + { + name = "socket" + hostname = ${?LOGSERVER_HOSTNAME} + hostname = "localhost" + port = ${?LOGSERVER_PORT} + port = 6000 + }, + { + name = "file", + pattern = null + }, + { + name = "console" + pattern = null + } + ] + defaultAppender = ${?DEFAULT_LOGGER} + defaultAppender = file } diff --git a/engine/launcher/src/main/resources/launcher.logback.xml b/engine/launcher/src/main/resources/launcher.logback.xml deleted file mode 100644 index c83d8ec4956c..000000000000 --- a/engine/launcher/src/main/resources/launcher.logback.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - $launcher.logRoot}/${launcher.logPrefix}-%d{yyyy-MM-dd}.log - false - true - - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n - - - - - - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n - - - - - ${logging-server.host} - ${logging-server.port} - 10000 - true - - - - - - - - diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala index de92e2c50931..c46a9269f2fe 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/GlobalCLIOptions.scala @@ -1,14 +1,8 @@ package org.enso.launcher.cli -import org.enso.cli.arguments.{Argument, OptsParseError} import org.enso.launcher.cli.GlobalCLIOptions.InternalOptions -import org.enso.logger.ColorMode.{Always, Auto, Never} -import org.enso.logger.ColorMode import java.net.URI - -//import org.enso.loggingservice.ColorMode.{Always, Auto, Never} -//import org.enso.loggingservice.{ColorMode, LogLevel} import org.slf4j.event.Level /** Gathers settings set by the global CLI options. @@ -20,7 +14,6 @@ import org.slf4j.event.Level * printed * @param useJSON specifies if output should be in JSON format, if it is * supported (currently only the version command supports JSON) - * @param colorMode specifies if console output should contain colors * @param internalOptions options that are remembered to pass them to launcher * child processes */ @@ -28,7 +21,6 @@ case class GlobalCLIOptions( autoConfirm: Boolean, hideProgress: Boolean, useJSON: Boolean, - colorMode: ColorMode, // TODO: no longer supported internalOptions: InternalOptions ) @@ -36,7 +28,6 @@ object GlobalCLIOptions { val HIDE_PROGRESS = "hide-progress" val AUTO_CONFIRM = "auto-confirm" val USE_JSON = "json" - val COLOR_MODE = "color" /** Internal options that are remembered to pass them to launcher child * processes. @@ -78,39 +69,6 @@ object GlobalCLIOptions { val hideProgress = if (config.hideProgress) Seq(s"--$HIDE_PROGRESS") else Seq() val useJSON = if (config.useJSON) Seq(s"--$USE_JSON") else Seq() - autoConfirm ++ hideProgress ++ useJSON ++ - LauncherColorMode.toOptions( - config.colorMode - ) ++ config.internalOptions.toOptions - } -} - -object LauncherColorMode { - - /** [[Argument]] instance used to parse [[ColorMode]] from CLI. - */ - implicit val argument: Argument[ColorMode] = { - case "never" => Right(Never) - case "no" => Right(Never) - case "auto" => Right(Auto) - case "always" => Right(Always) - case "yes" => Right(Always) - case other => - OptsParseError.left( - s"Unknown color mode value `$other`. Supported values are: " + - s"never | no | auto | always | yes." - ) - } - - /** Creates command line options that can be passed to a launcher process to - * inherit our color mode. - */ - def toOptions(colorMode: ColorMode): Seq[String] = { - val name = colorMode match { - case Never => "never" - case Auto => "auto" - case Always => "always" - } - Seq(s"--${GlobalCLIOptions.COLOR_MODE}", name) + autoConfirm ++ hideProgress ++ useJSON ++ config.internalOptions.toOptions } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index c0941c62117a..1a7fba78c3c3 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -8,13 +8,11 @@ import org.enso.cli.arguments.Opts.implicits._ import org.enso.cli.arguments._ import org.enso.distribution.config.DefaultVersion import org.enso.distribution.config.DefaultVersion._ -import org.enso.launcher.cli.LauncherColorMode.argument import org.enso.launcher.distribution.DefaultManagers._ import org.enso.launcher.installation.DistributionInstaller import org.enso.launcher.installation.DistributionInstaller.BundleAction import org.enso.launcher.upgrade.LauncherUpgrader import org.enso.launcher.{cli, Launcher} -import org.enso.logger.ColorMode import org.enso.runtimeversionmanager.cli.Arguments._ import org.enso.runtimeversionmanager.runner.LanguageServerOptions import org.slf4j.event.Level @@ -628,17 +626,6 @@ object LauncherApplication { "variable.", showInUsage = false ) - val colorMode = - Opts - .aliasedOptionalParameter[ColorMode]( - GlobalCLIOptions.COLOR_MODE, - "colour", - "colors" - )( - "(auto | yes | always | no | never)", - "Specifies if colors should be used in the output, defaults to auto." - ) - .withDefault(ColorMode.Auto) val internalOpts = InternalOpts.topLevelOptions ( @@ -650,8 +637,7 @@ object LauncherApplication { hideProgress, logLevel, connectLogger, - noLogMasking, - colorMode + noLogMasking ) mapN { ( internalOptsCallback, @@ -662,18 +648,18 @@ object LauncherApplication { hideProgress, logLevel, connectLogger, - disableLogMasking, - colorMode + disableLogMasking ) => () => if (shouldEnsurePortable) { Launcher.ensurePortable() } + LauncherLogging.initLogger(logLevel) + val globalCLIOptions = cli.GlobalCLIOptions( autoConfirm = autoConfirm, hideProgress = hideProgress, useJSON = useJSON, - colorMode = colorMode, internalOptions = GlobalCLIOptions.InternalOptions( logLevel, connectLogger, diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index c7f7e2c494a8..2cf4fd081e81 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -4,14 +4,14 @@ import java.nio.file.Path import org.enso.launcher.distribution.DefaultManagers import org.slf4j.event.Level -import org.enso.logging.LoggingCollectorHelper +import org.enso.logging.LoggingSetupHelper import org.enso.logging.LoggingServiceManager import scala.concurrent.ExecutionContext.Implicits.global /** Manages setting up the logging service within the launcher. */ -object LauncherLogging extends LoggingCollectorHelper { +object LauncherLogging extends LoggingSetupHelper { /** @inheritdoc */ override val defaultLogLevel: Level = Level.WARN @@ -23,8 +23,6 @@ object LauncherLogging extends LoggingCollectorHelper { override lazy val logPath: Path = DefaultManagers.distributionManager.paths.logs - override val logComponentName: String = "launcher" - /** Turns off the main logging service, falling back to just a stderr backend. * * This method should be called as part of uninstalling the distribution. The @@ -37,6 +35,6 @@ object LauncherLogging extends LoggingCollectorHelper { def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LoggingServiceManager.fallbackToLocalConsole(actualLogLevel, "launcher") + LoggingServiceManager.fallbackToLocalConsole(actualLogLevel) } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index 73d59e910803..0aaa11d2f831 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -1,7 +1,7 @@ package org.enso.launcher.cli import com.typesafe.scalalogging.Logger -import org.enso.logger.LoggerContextSetup +import org.enso.logger.LoggerSetup import org.enso.cli.CLIOutput import org.enso.launcher.distribution.DefaultManagers import org.enso.launcher.upgrade.LauncherUpgrader @@ -9,12 +9,8 @@ import org.enso.launcher.upgrade.LauncherUpgrader /** Defines the entry point for the launcher. */ object Main { - private def setup(): Unit = { - System.setProperty( - "org.apache.commons.logging.Log", - "org.apache.commons.logging.impl.NoOpLog" - ) - LoggerContextSetup.setup("launcher") + private def initLogger(): Unit = { + LoggerSetup.setupNoOpAppender() } private def runAppHandlingParseErrors(args: Array[String]): Int = @@ -32,7 +28,7 @@ object Main { /** Entry point of the application. */ def main(args: Array[String]): Unit = { - setup() + initLogger() val exitCode = try { LauncherUpgrader.recoverUpgradeRequiredErrors(args) { diff --git a/engine/launcher/src/test/resources/application.conf b/engine/launcher/src/test/resources/application.conf index af4e27167781..6091d62e22ad 100644 --- a/engine/launcher/src/test/resources/application.conf +++ b/engine/launcher/src/test/resources/application.conf @@ -1 +1,12 @@ logging-service.test-log-level = warning + +logging-service { + appenders = [ + { + name = "console" + pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" + } + ] + defaultAppender = console + log-level = "warn" +} diff --git a/engine/runner/src/main/resources/runner.logback.xml b/engine/runner/src/main/resources/runner.logback.xml deleted file mode 100644 index e97fd581a711..000000000000 --- a/engine/runner/src/main/resources/runner.logback.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n - - - - - ${logging-server.host} - ${logging-server.port} - 10000 - true - - - - - - diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 4bb987e5609e..4b7ed8b1a9a3 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -1,6 +1,7 @@ package org.enso.runner -import org.enso.logger.{Converter, JavaLoggingForwarder} +import org.enso.logger.Converter +import org.enso.logger.JulHandler import org.enso.polyglot.debugger.{ DebugServerInfo, DebuggerSessionManagerEndpoint @@ -87,7 +88,7 @@ class ContextFactory { RuntimeOptions.LOG_LEVEL, logLevelName ) - .logHandler(new JavaLoggingForwarder()) + .logHandler(new JulHandler()) .build new PolyglotContext(context) } diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index 9fe8f9438953..5b715775518c 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -9,8 +9,8 @@ import org.slf4j.event.Level import scala.util.{Failure, Success} import scala.concurrent.Future -import java.io.InputStream -import org.enso.logger.LoggerContextSetup +import org.enso.logger.LoggerSetup +import org.enso.logger.config.LoggingServiceConfig /** Manages setting up the logging service within the runner. */ @@ -33,24 +33,26 @@ object RunnerLogging { ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) - val loggingConfig = this.getClass.getResourceAsStream("/runner.logback.xml") + val loggingConfig = LoggingServiceConfig.parseConfig() val loggerSetup = connectionUri match { case Some(uri) => - Future( - LoggerContextSetup.setup( + Future { + LoggerSetup.setupSocketAppender( logLevel, - "runner", - loggingConfig, - "forward-via-socket", uri.getHost(), - uri.getPort() + uri.getPort(), + loggingConfig + ) + } + .map(success => + if (success) () + else throw new RuntimeException("setup failed") ) - ) .map { _ => logger.trace("Connected to logging service at [{}].", uri) } .recoverWith { _ => - logger.error( + System.err.println( "Failed to connect to the logging service server, " + "falling back to local logging." ) @@ -61,7 +63,6 @@ object RunnerLogging { } loggerSetup.onComplete { case Failure(exception) => - System.err.println(s"Failed to initialize logging: $exception") exception.printStackTrace() case Success(_) => } @@ -69,16 +70,12 @@ object RunnerLogging { private def setupLocalLogger( @unused logLevel: Level, - logbackConfig: InputStream + config: LoggingServiceConfig ): Future[Unit] = { Future.successful( - LoggerContextSetup.setup( + LoggerSetup.setupConsoleAppender( logLevel, - "runner", - logbackConfig, - "console", - null, - 0 + config ) ) } @@ -88,6 +85,6 @@ object RunnerLogging { /** Shuts down the logging service gracefully. */ def tearDown(): Unit = { - LoggerContextSetup.teardown() + LoggerSetup.teardown() } } diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala index 44031d4bdfd0..6dabb9cac059 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala @@ -52,7 +52,7 @@ private class DefaultPackageRepository( notificationHandler: NotificationHandler ) extends PackageRepository { - private val logger = Logger[DefaultPackageRepository] + private lazy val logger = Logger[DefaultPackageRepository] implicit private val fs: TruffleFileSystem = new TruffleFileSystem private val packageManager = new PackageManager[TruffleFile] diff --git a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala index 3125140b9591..c9618af37678 100644 --- a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala +++ b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala @@ -2,7 +2,7 @@ package org.enso.distribution.config import com.typesafe.scalalogging.Logger import io.circe.syntax._ -import io.circe.{yaml, Json} +import io.circe.{Json, yaml} import org.enso.distribution.DistributionManager import org.enso.distribution.FileSystem.PathSyntax @@ -12,7 +12,7 @@ import scala.util.{Failure, Success, Try, Using} /** Manages the global configuration of the distribution. */ class GlobalConfigurationManager(distributionManager: DistributionManager) { - private val logger = Logger[this.type] + private lazy val logger = Logger[GlobalConfigurationManager] /** Location of the global configuration file. */ def configLocation: Path = diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java new file mode 100644 index 000000000000..fbf9247f5e5d --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -0,0 +1,32 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public abstract class Appender { + + private final Config raw; + + public Appender(Config raw) { + this.raw = raw; + } + + public abstract String getName(); + + public static Appender parse(Config config) { + if (config != null) { + switch (config.getString("name")) { + case "file": + return FileAppender.parse(config); + case "socket": + return SocketAppender.parse(config); + default: + return ConsoleAppender.parse(config); + } + } + return null; + } + + public Config getConfig() { + return this.raw; + } +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java new file mode 100644 index 000000000000..060fb25466a8 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java @@ -0,0 +1,27 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public class ConsoleAppender extends Appender { + + private final String pattern; + + private ConsoleAppender(String pattern, Config config) { + super(config); + this.pattern = pattern; + } + + public static ConsoleAppender parse(Config config) { + String pattern = config.hasPath("pattern") ? config.getString("pattern") : null; + return new ConsoleAppender(pattern, config); + } + + public String getPattern() { + return pattern; + } + + @Override + public String getName() { + return "console"; + } +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java new file mode 100644 index 000000000000..bcb072cc2720 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -0,0 +1,40 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public class FileAppender extends Appender { + + private static String immediateFlushKey = "immediate-flush"; + private static String appendKey = "append"; + + private static String patternKey = "pattern"; + private final String name; + private final boolean append; + private final boolean immediateFlush; + private final String pattern; + + private FileAppender(boolean append, boolean immediateFlush, String pattern, Config config) { + super(config); + this.name = "file"; + this.append = append; + this.immediateFlush = immediateFlush; + this.pattern = pattern; + } + + public static Appender parse(Config config) { + boolean append = config.hasPath(appendKey) ? config.getBoolean(appendKey) : true; + boolean immediateFlush = + config.hasPath(immediateFlushKey) ? config.getBoolean("immediate-flush") : false; + String pattern = config.hasPath(patternKey) ? config.getString(patternKey) : null; + return new FileAppender(append, immediateFlush, pattern, config); + } + + @Override + public String getName() { + return name; + } + + public String getPattern() { + return pattern; + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java similarity index 55% rename from lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java rename to lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java index 414be05d96aa..75312b92f9e0 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Loggers.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java @@ -5,30 +5,31 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.slf4j.event.Level; public class Loggers { - private Map loggers; + private Map loggers; - private Loggers(Map loggers) { + private Loggers(Map loggers) { this.loggers = loggers; } - public Set> entrySet() { + public Set> entrySet() { return loggers.entrySet(); } public static Loggers parse(Config config) { // LinkedHashMap ensures that wildcard loggers are de-prioritized - Map loggers = new LinkedHashMap<>(); - Map fallbacks = new LinkedHashMap<>(); + Map loggers = systemLoggers(); + Map fallbacks = new LinkedHashMap<>(); config .entrySet() .forEach( entry -> { String key = entry.getKey(); String v = config.getString(key); - ch.qos.logback.classic.Level level = ch.qos.logback.classic.Level.toLevel(v); + Level level = Level.valueOf(v.toUpperCase()); String normalizedKey = normalizeKey(key); if (normalizedKey.endsWith("*")) { @@ -44,6 +45,23 @@ public static Loggers parse(Config config) { return new Loggers(loggers); } + private static Map systemLoggers() { + Map loggers = new LinkedHashMap<>(); + System.getProperties().forEach((keyObj, value) -> { + String key = keyObj.toString(); + if (key.endsWith(SYS_PROP_SUFFIX)) { + int idx = key.lastIndexOf(SYS_PROP_SUFFIX); + String loggerName = key.substring(0, idx); + try { + loggers.put(loggerName, Level.valueOf(value.toString().toUpperCase())); + } catch (IllegalArgumentException e) { + System.err.println("Invalid log level `" + value + "` for " + loggerName + ". Skipping..."); + } + } + }); + return loggers; + } + @Override public java.lang.String toString() { return String.join( @@ -56,4 +74,5 @@ public java.lang.String toString() { private static String normalizeKey(String key) { return key.replace("'", "").replace("\"", ""); } + private static String SYS_PROP_SUFFIX = ".Logger.level"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java new file mode 100644 index 000000000000..9f6e23c21e61 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java @@ -0,0 +1,116 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigException; +import com.typesafe.config.ConfigFactory; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class LoggingServiceConfig { + public static final String configurationRoot = "logging-service"; + public static final String serverKey = "server"; + public static final String loggersKey = "logger"; + public static final String appendersKey = "appenders"; + public static final String defaultAppenderKey = "defaultAppender"; + public static final String logLevelKey = "log-level"; + + private final Loggers loggers; + private final Map appenders; + + private final String defaultAppenderName; + private final Optional logLevel; + private final Server server; + + private LoggingServiceConfig( + Loggers loggers, + Optional logLevel, + Map appenders, + String defaultAppender, + Server server) { + this.loggers = loggers; + this.appenders = appenders; + this.defaultAppenderName = defaultAppender; + this.logLevel = logLevel; + this.server = server; + } + + public static LoggingServiceConfig parseConfig() { + var empty = ConfigFactory.empty().atKey(configurationRoot); + var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); + Server server; + if (root.hasPath(serverKey)) { + Config serverConfig = root.getConfig(serverKey); + server = Server.parse(serverConfig); + } else { + server = null; + } + Map appendersMap = new HashMap<>(); + if (root.hasPath(appendersKey)) { + List configs = root.getConfigList(appendersKey); + for (Config c : configs) { + Appender a = Appender.parse(c); + appendersMap.put(a.getName(), a); + } + } + Loggers loggers; + if (root.hasPath(loggersKey)) { + loggers = Loggers.parse(root.getConfig(loggersKey)); + } else { + loggers = null; + } + return new LoggingServiceConfig( + loggers, + getStringOpt(logLevelKey, root), + appendersMap, + root.getString(defaultAppenderKey), + server); + } + + public Loggers getLoggers() { + return loggers; + } + + public Appender getAppender() { + return appenders.get(defaultAppenderName); + } + + public Appender getAppender(String name) { + return appenders.get(name); + } + + public boolean loggingServerNeedsBoot() { + return server != null && server.start(); + } + + private static Optional getStringOpt(String key, Config config) { + try { + return Optional.ofNullable(config.getString(key)); + } catch (ConfigException.Missing missing) { + return Optional.empty(); + } + } + + public Optional getLogLevel() { + return logLevel; + } + + public Server getServer() { + return server; + } + + @Override + public String toString() { + return "Loggers: " + + loggers + + ", appenders: " + + String.join(",", appenders.keySet()) + + ", default-appender: " + + (defaultAppenderName == null ? "unknown" : defaultAppenderName) + + ", logLevel: " + + logLevel.orElseGet(() -> "default") + + ", server: " + + server; + } +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java new file mode 100644 index 000000000000..9062ef2b0d16 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -0,0 +1,23 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public class SentryAppender extends Appender { + private String name; + private String dsn; + + private SentryAppender(String dsn, Config config) { + super(config); + this.name = "sentry"; + this.dsn = dsn; + } + + public static Appender parse(Config config) { + return new SentryAppender(config.getString("dsn"), config); + } + + @Override + public String getName() { + return name; + } +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java new file mode 100644 index 000000000000..f0e29277bdac --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java @@ -0,0 +1,15 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; + +public record Server(String hostname, int port, Appender appender, Boolean start) { + + public static Server parse(Config config) { + int port = config.getInt("port"); + String hostname = config.getString("hostname"); + Appender appender = Appender.parse(config.getConfig("appender")); + boolean start = config.getBoolean("start"); + return new Server(hostname, port, appender, start); + } + +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java new file mode 100644 index 000000000000..4100531532b7 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -0,0 +1,43 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; +import java.net.URI; + +public class SocketAppender extends Appender { + private String name; + private String hostname; + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + private int port; + private int reconnectionDelay; + private boolean includeCallerData; + + private SocketAppender(String hostname, int port, Config config) { + super(config); + this.name = "socket"; + this.hostname = hostname; + this.port = port; + this.reconnectionDelay = 10000; + this.includeCallerData = true; + } + + @Override + public String getName() { + return name; + } + + public boolean isSameTarget(URI uri) { + return uri.getHost().equals(hostname) && uri.getPort() == port; + } + + public static Appender parse(Config config) { + return new SocketAppender(config.getString("hostname"), config.getInt("port"), config); + } +} diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java deleted file mode 100644 index baedc3d86428..000000000000 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JavaLoggingForwarder.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.enso.logger; - -import static ch.qos.logback.core.net.SocketConnector.ExceptionHandler; -import static java.util.logging.Level.*; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.SocketAppender; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.spi.ThrowableProxy; -import ch.qos.logback.core.ConsoleAppender; -import ch.qos.logback.core.net.DefaultSocketConnector; -import ch.qos.logback.core.net.SocketConnector; -import java.io.IOException; -import java.net.ConnectException; -import java.net.InetAddress; -import java.net.Socket; -import java.util.logging.Handler; -import java.util.logging.LogRecord; -import org.enso.logger.config.LoggingService; -import org.enso.logger.config.Server; - -/** - * JavaLoggingForwarder will forward all java.util.logging.LogRecord to a Logback's appender. - * Forwarding is dependent upon the presence of the coordinates for the central logging server and - * wheteher it is up and running. - */ -public class JavaLoggingForwarder extends Handler { - private SocketConnector connector; - - private ch.qos.logback.core.Appender appender; - private boolean started; - - public JavaLoggingForwarder() { - this(LoggingService.parseConfig().getServer()); - } - - public JavaLoggingForwarder(Server config) { - this(config.hostname(), config.port()); - } - - public JavaLoggingForwarder(int port) { - this("localhost", port); - } - - public JavaLoggingForwarder(String host, int port) { - try { - InetAddress address = - host.equals("localhost") ? InetAddress.getLocalHost() : InetAddress.getByName(host); - connector = new DefaultSocketConnector(address, port, 0, 10000); - connector.setExceptionHandler(new FallbackExceptionHandler(1)); - } catch (Throwable e) { - connector = null; - } - boolean enabled = canEstablishSocketConnection(); - if (enabled) { - SocketAppender socketAppender = new SocketAppender(); - socketAppender.setPort(port); - socketAppender.setRemoteHost(host); - socketAppender.setIncludeCallerData(false); - LoggerContext ctx = new LoggerContext(); - socketAppender.setContext(new LoggerContext()); - appender = socketAppender; - } else { - appender = new ConsoleAppender(); - } - appender.addFilter(ApplicationFilter.fromLoggers(LoggingService.parseConfig().getLoggers())); - - started = false; - } - - @Override - public void publish(LogRecord record) { - if (!started) { - synchronized (this) { - if (!started) { - started = true; - appender.start(); - } - } - } - appender.doAppend(toEvent(record)); - } - - private boolean canEstablishSocketConnection() { - if (connector == null) return false; - try { - Socket socket = connector.call(); - boolean test = socket != null; - if (test) { - socket.close(); - } - return test; - } catch (InterruptedException | IOException e) { - System.err.println("Failed to establish connection: " + e.getCause().getMessage()); - return false; - } catch (RuntimeException e) { - if (!(e.getCause() instanceof ConnectException)) { - System.err.println("Failed to establish connection: " + e.getCause().getMessage()); - } - return false; - } - } - - @Override - public void flush() {} - - @Override - public void close() throws SecurityException { - // flush all remaining events - if (appender != null) appender.stop(); - } - - private ILoggingEvent toEvent(LogRecord record) { - LoggingEvent event = new LoggingEvent(); - event.setLoggerName(record.getLoggerName()); - event.setLevel(toLogbackLevel(record.getLevel())); - // Replace jul-specific placeholders with ones used by everybody else - event.setMessage(record.getMessage().replaceAll("\\{\\d+\\}", "{}")); - Object[] args; - if (record.getParameters().length == 1 && record.getParameters()[0] instanceof Object[]) { - args = (Object[]) (record.getParameters()[0]); - } else { - args = record.getParameters(); - } - event.setArgumentArray(args); - event.setInstant(record.getInstant()); - if (record.getThrown() != null) { - event.setThrowableProxy(new ThrowableProxy(record.getThrown())); - } - event.prepareForDeferredProcessing(); - return event; - } - - private static ch.qos.logback.classic.Level toLogbackLevel(java.util.logging.Level julLevel) { - if (julLevel.intValue() == SEVERE.intValue()) return ch.qos.logback.classic.Level.ERROR; - else if (julLevel.intValue() == INFO.intValue()) return ch.qos.logback.classic.Level.INFO; - else if (julLevel.intValue() == WARNING.intValue()) return ch.qos.logback.classic.Level.WARN; - else if (julLevel.intValue() == ALL.intValue()) return ch.qos.logback.classic.Level.ALL; - else if (julLevel.intValue() == FINE.intValue()) return ch.qos.logback.classic.Level.DEBUG; - else if (julLevel.intValue() == FINER.intValue()) return ch.qos.logback.classic.Level.TRACE; - else if (julLevel.intValue() == FINEST.intValue()) return ch.qos.logback.classic.Level.TRACE; - else return ch.qos.logback.classic.Level.OFF; - } - - /** - * Local exception handler that propagates a failure to establish the connection. That way, - * establishing a socket connection is not attempted infinitely on failure. - */ - private class FallbackExceptionHandler implements ExceptionHandler { - int retries; - - public FallbackExceptionHandler(int retries) { - this.retries = retries; - } - - public FallbackExceptionHandler() { - this(2); - } - - public void connectionFailed(SocketConnector connector, Exception ex) { - if (retries == 0) { - throw new RuntimeException(ex); - } else { - retries = retries - 1; - } - } - } -} diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java new file mode 100644 index 000000000000..15d1a78d01d4 --- /dev/null +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java @@ -0,0 +1,62 @@ +package org.enso.logger; + +import static java.util.logging.Level.*; + +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JulHandler extends Handler { + + private Formatter formattter; + + public JulHandler() { + this.formattter = new SimpleFormatter(); + } + + @Override + public void publish(LogRecord record) { + Logger logger = LoggerFactory.getLogger(record.getLoggerName()); + java.util.logging.Level julLevel = record.getLevel(); + String msg; + boolean hasThrowable; + if (record.getThrown() != null) { + hasThrowable = true; + msg = formattter.formatMessage(record); + } else { + hasThrowable = false; + msg = record.getMessage().replaceAll("\\{\\d+\\}", "{}"); + } + if (julLevel.intValue() == SEVERE.intValue()) { + if (hasThrowable) logger.error(msg, record.getThrown()); + else logger.error(msg, record.getParameters()); + } else if (julLevel.intValue() == INFO.intValue()) { + if (hasThrowable) logger.info(msg, record.getThrown()); + else logger.info(msg, record.getParameters()); + } else if (julLevel.intValue() == WARNING.intValue()) { + if (hasThrowable) logger.warn(msg, record.getThrown()); + else logger.warn(msg, record.getParameters()); + } else if (julLevel.intValue() == ALL.intValue()) { + if (hasThrowable) logger.trace(msg, record.getThrown()); + else logger.trace(msg, record.getParameters()); + } else if (julLevel.intValue() == FINE.intValue()) { + if (hasThrowable) logger.debug(msg, record.getThrown()); + else logger.debug(msg, record.getParameters()); + } else if (julLevel.intValue() == FINER.intValue()) { + if (hasThrowable) logger.trace(msg, record.getThrown()); + else logger.trace(msg, record.getParameters()); + } else if (julLevel.intValue() == FINEST.intValue()) { + if (hasThrowable) logger.trace(msg, record.getThrown()); + else logger.trace(msg, record.getParameters()); + } + } + + @Override + public void flush() {} + + @Override + public void close() throws SecurityException {} +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java index 296b0f17cc79..2dec87138ecb 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java @@ -1,5 +1,6 @@ package org.enso.logger; +import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.spi.FilterReply; @@ -16,7 +17,8 @@ private ApplicationFilter(Loggers loggers) { public FilterReply decide(ILoggingEvent event) { for (var entry : loggers.entrySet()) { if (event.getLoggerName().startsWith(entry.getKey())) { - if (event.getLevel().isGreaterOrEqual(entry.getValue())) { + Level loggerLevel = Level.convertAnSLF4JLevel(entry.getValue()); + if (event.getLevel().isGreaterOrEqual(loggerLevel)) { return FilterReply.NEUTRAL; } else { return FilterReply.DENY; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java deleted file mode 100644 index 521c30c7f691..000000000000 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerContextSetup.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.enso.logger; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.util.StatusPrinter; -import java.io.InputStream; -import org.enso.logger.config.Loggers; -import org.enso.logger.config.LoggingService; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -public class LoggerContextSetup { - - public static Boolean setup(String componentName) { - var resource = - LoggerContextSetup.class.getResourceAsStream("/" + componentName + ".logback.xml"); - return resource != null - ? setup(null, componentName, resource, LoggingService.parseConfig()) - : false; - } - - public static Boolean setup(Level level, String componentName, InputStream customLogConfig) { - return setup(level, componentName, customLogConfig, LoggingService.parseConfig()); - } - - public static Boolean setup( - Level logLevel, String componentName, InputStream customLogConfig, LoggingService appConfig) { - var serverConfig = appConfig.getServer(); - var appenderName = appConfig.getAppender(); - return setup( - logLevel, - componentName, - customLogConfig, - appenderName, - serverConfig.hostname(), - serverConfig.port(), - appConfig.getLoggers()); - } - - public static Boolean setup( - Level logLevel, - String componentName, - InputStream customLogConfig, - String appenderName, - String hostname, - int port) { - return setup(logLevel, componentName, customLogConfig, appenderName, hostname, port, null); - } - - public static Boolean setup( - Level logLevel, - String componentName, - InputStream customLogConfig, - String appenderName, - String hostname, - int port, - Loggers loggers) { - var context = (LoggerContext) LoggerFactory.getILoggerFactory(); - if (logLevel != null) { - var logbackLevel = ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel); - System.setProperty(componentName + ".logLevel", logbackLevel.toString().toLowerCase()); - } - if (appenderName != null) { - System.setProperty(componentName + ".appender", appenderName); - } - if (hostname != null) { - System.setProperty("logging-server.host", hostname); - } - if (port != 0) { - System.setProperty("logging-server.port", String.valueOf(port)); - } - try { - var configurator = new JoranConfigurator(); - configurator.setContext(context); - // Call context.reset() to clear any previous configuration, e.g. default - // configuration. - context.reset(); - configurator.doConfigure(customLogConfig); - var rootLogger = context.getLogger("root"); - var appender = rootLogger.getAppender(appenderName); - if (appender != null) { - if (loggers != null) { - var filter = ApplicationFilter.fromLoggers(loggers); - appender.addFilter(filter); - } - } - /* - // TODO: report if cannot customize the appender - else if (appenderName != null) { - //System.err.println( - // "Failed to apply custom log levels for application loggers' in " + appenderName + " for " + componentName); - } - */ - - return true; - } catch (JoranException je) { - je.printStackTrace(); - StatusPrinter.printInCaseOfErrorsOrWarnings(context); - System.err.println("Failed to setup Logback configuration"); - return false; - } - } - - public static void teardown() { - var context = (LoggerContext) LoggerFactory.getILoggerFactory(); - context.stop(); - } -} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java new file mode 100644 index 000000000000..54308dde7525 --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java @@ -0,0 +1,183 @@ +package org.enso.logger; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.net.SocketAppender; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.helpers.NOPAppender; + +import java.io.File; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.enso.logger.config.LoggingServiceConfig; +import org.enso.logger.config.Appender; +import org.enso.logger.config.Loggers; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +public class LoggerSetup { + + private static final String defaultPattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n"; + + private static final String fileAppenderKey = "file"; + private static final String socketAppenderKey = "socket"; + private static final String consoleAppenderKey = "console"; + + public static Boolean setup() { + LoggingServiceConfig config = LoggingServiceConfig.parseConfig(); + return setup(config); + } + + public static Boolean setup(LoggingServiceConfig config) { + Level defaultLogLevel = config.getLogLevel().map(name -> Level.valueOf(name.toUpperCase())).orElseGet(() -> Level.ERROR); + return setup(defaultLogLevel, config); + } + + public static Boolean setup(Level logLevel) { + return setup(logLevel, LoggingServiceConfig.parseConfig()); + } + + public static Boolean setup(Level logLevel, LoggingServiceConfig config) { + Appender defaultAppender = config.getAppender(); + if (defaultAppender != null) { + switch (defaultAppender.getName()) { + case fileAppenderKey: + return setupFileAppender(logLevel, null, null, config); + case socketAppenderKey: + return setupSocketAppender(logLevel, defaultAppender.getConfig().getString("hostname"), defaultAppender.getConfig().getInt("port"), config); + default: + return setupConsoleAppender(logLevel, config); + } + } else { + return setupConsoleAppender(logLevel, config); + } + } + public static Boolean setupSocketAppender( + Level logLevel, + String hostname, + int port, + LoggingServiceConfig config) { + LoggerAndContext env = contextInit(logLevel, config); + + SocketAppender socketAppender = new SocketAppender(); + socketAppender.setName("enso-socket"); + socketAppender.setIncludeCallerData(true); + socketAppender.setRemoteHost(hostname); + socketAppender.setPort(port); + + env.finalizeAppender(socketAppender); + + return true; + } + + + public static Boolean setupFileAppender( + Level logLevel, + Path logRoot, + String logPrefix, + LoggingServiceConfig config + ) { + LoggerAndContext env = contextInit(logLevel, config); + + String configPattern = ((org.enso.logger.config.ConsoleAppender)config.getAppender(fileAppenderKey)).getPattern(); + final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setPattern(configPattern == null ? defaultPattern : configPattern); + env.finalizeEncoder(encoder); + + FileAppender fileAppender = new FileAppender<>(); + fileAppender.setName("enso-file"); + fileAppender.setAppend(true); + fileAppender.setImmediateFlush(true); + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String currentDate = LocalDate.now().format(dtf); + String fullFilePath; + if (logRoot == null || logPrefix == null) { + fullFilePath = "enso-" + currentDate + ".log"; + } else { + fullFilePath = logRoot.toAbsolutePath() + File.separator + logPrefix + "-" + currentDate + ".log"; + } + fileAppender.setEncoder(encoder); + fileAppender.setFile(fullFilePath); + + env.finalizeAppender(fileAppender); + return true; + } + + public static Boolean setupConsoleAppender(Level logLevel, LoggingServiceConfig config) { + LoggerAndContext env = contextInit(logLevel, config); + + String consolePattern; + if (config != null) { + consolePattern = ((org.enso.logger.config.ConsoleAppender)config.getAppender(consoleAppenderKey)).getPattern(); + } else { + consolePattern = null; + } + final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setPattern(consolePattern == null ? defaultPattern : consolePattern); + env.finalizeEncoder(encoder); + + ConsoleAppender consoleAppender = new ConsoleAppender<>(); + consoleAppender.setName("enso-console"); + consoleAppender.setEncoder(encoder); + + env.finalizeAppender(consoleAppender); + return true; + } + + public static Boolean setupNoOpAppender() { + LoggerAndContext env = contextInit(Level.ERROR, null); + + NOPAppender appender = new NOPAppender<>(); + appender.setName("enso-noop"); + + env.finalizeAppender(appender); + return true; + } + + public static void teardown() { + // TODO: disable whatever appender is now in place and replace it with console + var context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.stop(); + } + + private static LoggerAndContext contextInit(Level level, LoggingServiceConfig config) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.reset(); + context.setName("enso-custom"); + Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); + + Filter filter; + Loggers loggers = config != null ? config.getLoggers() : null; + if (loggers != null) { + filter = ApplicationFilter.fromLoggers(loggers); + } else { + filter = null; + } + return new LoggerAndContext(level, context, rootLogger, filter); + } + + private record LoggerAndContext(Level level, LoggerContext ctx, Logger logger, Filter filter) { + + void finalizeEncoder(ch.qos.logback.core.encoder.Encoder encoder) { + encoder.setContext(ctx); + encoder.start(); + } + void finalizeAppender(ch.qos.logback.core.Appender appender) { + logger.setLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(level)); + if (filter != null) { + appender.addFilter(filter); + filter.setContext(ctx); + filter.start(); + } + appender.setContext(ctx); + appender.start(); + logger.addAppender(appender); + } + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java deleted file mode 100644 index 77559a9b82f0..000000000000 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/LoggingService.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.enso.logger.config; - -import com.typesafe.config.Config; -import com.typesafe.config.ConfigException; -import com.typesafe.config.ConfigFactory; -import java.util.Optional; - -public class LoggingService { - public static final String configurationRoot = "logging-service"; - public static final String serverKey = "server"; - public static final String loggersKey = "logger"; - public static final String appenderKey = "appender"; - public static final String testLogLevelKey = "test-log-level"; - - private Loggers loggers; - private String appender; - private Optional testLogLevel; - private Server server; - - private LoggingService( - Loggers loggers, Optional testLogLevel, String appender, Server server) { - this.loggers = loggers; - this.appender = appender; - this.testLogLevel = testLogLevel; - this.server = server; - } - - public static LoggingService parseConfig() { - var empty = ConfigFactory.empty().atKey(configurationRoot); - var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); - Server server = Server.parse(root.getConfig(serverKey)); - Loggers loggers = Loggers.parse(root.getConfig(loggersKey)); - return new LoggingService( - loggers, getStringOpt(testLogLevelKey, root), root.getString(appenderKey), server); - } - - public Loggers getLoggers() { - return loggers; - } - - public String getAppender() { - return appender; - } - - private static Optional getStringOpt(String key, Config config) { - try { - return Optional.ofNullable(config.getString(key)); - } catch (ConfigException.Missing missing) { - return Optional.empty(); - } - } - - public Optional getTestLogLevel() { - return testLogLevel; - } - - public Server getServer() { - return server; - } - - @Override - public String toString() { - return "Loggers: " - + loggers - + ", appender: " - + appender - + ", testLogLevel: " - + testLogLevel.orElseGet(() -> "unknown") - + ", server: " - + server; - } -} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java deleted file mode 100644 index 84193efb93b9..000000000000 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/config/Server.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.enso.logger.config; - -import com.typesafe.config.Config; - -public record Server(String hostname, int port) { - - public static Server parse(Config config) { - int port = config.getInt("port"); - String hostname = config.getString("hostname"); - return new Server(hostname, port); - } -} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java b/lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java similarity index 56% rename from lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java rename to lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java index bd0d5cbe0248..a68a4722a368 100644 --- a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ForwardToServer.java +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java @@ -10,9 +10,6 @@ import org.slf4j.event.Level; class ForwardToServer extends LoggingService { - private static final String rollingFileAppender = "rolling-file"; - private static final String consoleAppender = "console"; - private static final String socketForwarderAppender = "socket-forwarder"; private int port; private SimpleSocketServer logServer; @@ -22,14 +19,14 @@ public ForwardToServer(int port) { this.logServer = null; } - public URI logToFile(Level level, Path path, String prefix) + public URI logToFile(Level level, Path path, String prefix, String appenderName) throws URISyntaxException, JoranException { var lc = new LoggerContext(); var configurator = new JoranConfigurator(); System.setProperty("logging-server.logRoot", path.toAbsolutePath().toString()); System.setProperty("logging-server.logPrefix", prefix); System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); - System.setProperty("logging-server.appender", rollingFileAppender); + System.setProperty("logging-server.appender", appenderName); configurator.setContext(lc); configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); logServer = new SimpleSocketServer(lc, port); @@ -37,20 +34,6 @@ public URI logToFile(Level level, Path path, String prefix) return new URI(null, null, "localhost", port, null, null, null); } - public void forwardToSocket(Level level, String targetHostname, int targetPort) - throws JoranException { - var lc = new LoggerContext(); - var configurator = new JoranConfigurator(); - configurator.setContext(lc); - System.setProperty("logging-server.targetHost", targetHostname); - System.setProperty("logging-server.targetPort", String.valueOf(targetPort)); - System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); - System.setProperty("logging-server.appender", socketForwarderAppender); - configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); - logServer = new SimpleSocketServer(lc, port); - logServer.start(); - } - public boolean isSetup() { return logServer != null; } diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java new file mode 100644 index 000000000000..84670389dea3 --- /dev/null +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java @@ -0,0 +1,7 @@ +package org.enso.logging; + +public class LoggerInitializationFailed extends RuntimeException { + public LoggerInitializationFailed() { + super("Logger initialization failed"); + } +} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java similarity index 100% rename from lib/scala/logging-socket-collector/src/main/java/org/enso/logging/LoggingService.java rename to lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java diff --git a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml b/lib/scala/logging-server/src/main/resources/logging-server.logback.xml similarity index 90% rename from lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml rename to lib/scala/logging-server/src/main/resources/logging-server.logback.xml index 5e66d871fc0d..42bcf414e2e6 100644 --- a/lib/scala/logging-socket-collector/src/main/resources/logging-server.logback.xml +++ b/lib/scala/logging-server/src/main/resources/logging-server.logback.xml @@ -1,6 +1,6 @@ - + ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.log ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.%i.log.gz @@ -8,7 +8,7 @@ 30 2GB - false + true true [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala similarity index 60% rename from lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala rename to lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala index 50791d8a34d4..acea30447a15 100644 --- a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -1,7 +1,10 @@ package org.enso.logging +import org.enso.logger.LoggerSetup + import java.net.URI import org.slf4j.event.Level + import java.nio.file.Path import scala.concurrent.Future import scala.concurrent.ExecutionContext @@ -17,7 +20,8 @@ object LoggingServiceManager { logLevel: Level, port: Int, logPath: Path, - logFileSuffix: String + logFileSuffix: String, + appenderName: String )(implicit ec: ExecutionContext): Future[URI] = { if (loggingService != null) { throw new RuntimeException("logging service already setup") @@ -26,33 +30,17 @@ object LoggingServiceManager { val forwarder = new ForwardToServer(port) loggingService = forwarder Future { - forwarder.logToFile(logLevel, logPath, logFileSuffix) - } - } - } - - def setupConnection(logLevel: Level, uri: URI)(implicit - ec: ExecutionContext - ): Future[Unit] = { - if (loggingService != null) { - throw new RuntimeException("logging service already setup") - } else { - currentLevel = logLevel - val client = new ExternalLogger(uri) - loggingService = client - Future { - client.connect() + forwarder.logToFile(logLevel, logPath, logFileSuffix, appenderName) } } } - def fallbackToLocalConsole(logLevel: Level, componentName: String): Unit = { + def fallbackToLocalConsole(logLevel: Level): Unit = { if (loggingService != null) { loggingService.teardown() } - val local = new Local(componentName) - loggingService = local - local.start(logLevel) + LoggerSetup.setup(logLevel) + loggingService = null; } def teardown(): Unit = { diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala new file mode 100644 index 000000000000..3b84a97df7d8 --- /dev/null +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala @@ -0,0 +1,144 @@ +package org.enso.logging + +import org.slf4j.event.Level + +import java.net.URI +import java.nio.file.Path +import scala.annotation.unused +import scala.concurrent.Future +import scala.concurrent.ExecutionContext +import scala.concurrent.Promise +import scala.util.{Failure, Success} +import org.enso.logger.LoggerSetup +import org.enso.logger.config.{LoggingServiceConfig, SocketAppender} + +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt + +abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { + val defaultLogLevel: Level + + val logFileSuffix: String + + def logPath: Path + + def loggingServiceEndpoint(): Future[Option[URI]] = + loggingServiceEndpointPromise.future + + private val loggingServiceEndpointPromise = Promise[Option[URI]]() + + def initLogger(logLevel: Option[Level]): Unit = { + LoggerSetup.setupConsoleAppender(logLevel.getOrElse(Level.ERROR), null); + } + + def setupServerAndForwardLogs( + logLevel: Option[Level], + logMasking: Boolean, + profilingLog: Option[Path] + ): Unit = { + // Setup server at a given port + val config = LoggingServiceConfig.parseConfig() + if (config.loggingServerNeedsBoot()) { + val actualPort = config.getServer().port(); + val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + LoggingServiceManager + .setupServer(actualLogLevel, actualPort, logPath, logFileSuffix, config.getServer().appender().getName()) + .onComplete { + // fallback to whatever is the default appender + case Failure(exception) => + exception.printStackTrace() + setup(logLevel, None, logMasking, profilingLog, config) + case Success(uri) => + val result = LoggerSetup.setup(actualLogLevel, config) + + if (!result) { + loggingServiceEndpointPromise.failure( + new LoggerInitializationFailed() + ) + } else { + loggingServiceEndpointPromise.success(Some(uri)) + } + } + } else { + // just setup whatever is the default + val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + if (LoggerSetup.setup(actualLogLevel, config)) { + loggingServiceEndpointPromise.success(None) + } + } + } + + def setup( + logLevel: Option[Level], + connectToExternalLogger: Option[URI], + logMasking: Boolean, + profilingLog: Option[Path] + ): Unit = { + setup( + logLevel, + connectToExternalLogger, + logMasking, + profilingLog, + LoggingServiceConfig.parseConfig() + ); + } + + def setup( + logLevel: Option[Level], + connectToExternalLogger: Option[URI], + @unused logMasking: Boolean, + @unused profilingLog: Option[Path], + config: LoggingServiceConfig + ): Unit = { + val actualLogLevel = logLevel.getOrElse(defaultLogLevel) + + connectToExternalLogger match { + case Some(uri) => + var initialized = LoggerSetup.setupSocketAppender( + actualLogLevel, + uri.getHost(), + uri.getPort(), + config + ) + if (!initialized) { + // Fallback, try the default appender if it is not the socket appender to the same port + config.getAppender() match { + case defaultAppender: SocketAppender => + if (!defaultAppender.isSameTarget(uri)) { + initialized = LoggerSetup.setup(actualLogLevel, config) + if (!initialized) { + // Fallback to console + initialized = + LoggerSetup.setupConsoleAppender(actualLogLevel, config); + } + } else { + // Fallback to console + initialized = + LoggerSetup.setupConsoleAppender(actualLogLevel, config); + } + case _ => + initialized = LoggerSetup.setup(actualLogLevel, config) + } + } + if (initialized) { + loggingServiceEndpointPromise.success(None) + } else { + loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()) + } + case None => + if (LoggerSetup.setup(actualLogLevel, config)) { + loggingServiceEndpointPromise.success(None) + } else { + loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()) + } + } + } + + def waitForSetup(): Unit = { + Await.ready(loggingServiceEndpointPromise.future, 5.seconds) + } + + def tearDown(): Unit = { + LoggingServiceManager.teardown() + } +} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java deleted file mode 100644 index 49769a13b2da..000000000000 --- a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/ExternalLogger.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.enso.logging; - -import java.net.ConnectException; -import java.net.URI; -import org.slf4j.event.Level; - -public class ExternalLogger extends LoggingService { - private URI targetURI; - - public ExternalLogger(URI uri) { - this.targetURI = uri; - } - - public void connect() throws ConnectException { - /* SocketAppender socketAppender = new SocketAppender(); - socketAppender.setPort(port); - socketAppender.setRemoteHost(host); - socketAppender.setIncludeCallerData(false);*/ - throw new ConnectException("failed to connect to " + targetURI); - } - - @Override - public void teardown() {} - - public boolean fallbackToConsole(Level level) { - return false; - } -} diff --git a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java b/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java deleted file mode 100644 index 3ac60ad14002..000000000000 --- a/lib/scala/logging-socket-collector/src/main/java/org/enso/logging/Local.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.enso.logging; - -import org.enso.logger.LoggerContextSetup; -import org.slf4j.event.Level; - -public class Local extends LoggingService { - - private String componentName; - - public Local(String componentName) { - this.componentName = componentName; - } - - @Override - public void teardown() { - LoggerContextSetup.teardown(); - } - - public void start(Level logLevel) { - var logConfig = this.getClass().getResourceAsStream("/" + componentName + ".logback.xml"); - LoggerContextSetup.setup(logLevel, componentName, logConfig, "console", null, 0); - } -} diff --git a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala b/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala deleted file mode 100644 index 4735c143d9fa..000000000000 --- a/lib/scala/logging-socket-collector/src/main/scala/org/enso/logging/LoggingCollectorHelper.scala +++ /dev/null @@ -1,145 +0,0 @@ -package org.enso.logging - -import org.slf4j.event.Level - -import java.io.InputStream -import java.net.URI -import java.nio.file.Path -import scala.annotation.unused -import scala.concurrent.Future -import scala.concurrent.ExecutionContext -import scala.concurrent.Promise - -import scala.util.{Failure, Success} -import org.enso.logger.LoggerContextSetup -import org.enso.logger.config.LoggingService - -import scala.concurrent.Await -import scala.concurrent.duration.DurationInt - -abstract class LoggingCollectorHelper(implicit - executionContext: ExecutionContext -) { - val defaultLogLevel: Level - - val logFileSuffix: String - - def logPath: Path - - def logComponentName: String - - def logConfiguration(): Option[InputStream] = { - val resource = - this.getClass.getResourceAsStream(s"/${logComponentName}.logback.xml") - Option.when(resource != null)(resource) - } - - protected def logLevelPropertyName: String = s"${logComponentName}.logLevel" - - protected def appenderPropertyName: String = s"${logComponentName}.appender" - - def loggingServiceEndpoint(): Future[Option[URI]] = Future.successful(None) - - private val loggingServiceEndpointPromise = Promise[Option[URI]]() - - def setup( - logLevel: Option[Level], - connectToExternalLogger: Option[URI], - @unused logMasking: Boolean, - @unused profilingLog: Option[Path] - ): Unit = { - val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - logConfiguration() - .map(conf => - Boolean.unbox( - LoggerContextSetup - .setup(actualLogLevel, logComponentName, conf, null, null, 0, null) - ) - ) - val loggingService = LoggingService.parseConfig() - //TODO: Masking.setup(logMasking) - val (host, port, serverNeedsInitialization) = - connectToExternalLogger match { - case Some(uri) => - (uri.getHost(), uri.getPort(), false) - case None => - ( - loggingService.getServer().hostname(), - loggingService.getServer().port(), - true - ) - } - - if (serverNeedsInitialization) { - LoggingServiceManager - .setupServer(actualLogLevel, port, logPath, logFileSuffix) - .onComplete { - case Failure(exception) => - exception.printStackTrace() - System.setProperty( - s"${logComponentName}.logRoot", - logPath.toAbsolutePath.toString - ) - System.setProperty( - s"${logComponentName}.logPrefix", - logFileSuffix + "-standalone" - ) - System.setProperty( - logLevelPropertyName, - actualLogLevel.toString.toLowerCase() - ) - // fallback to whatever is the default appender - case Success(uri) => - val appender = loggingService.getAppender() - System.setProperty(appenderPropertyName, appender) - System.setProperty("logging-server.host", host) - System.setProperty("logging-server.port", port.toString) - System.setProperty( - logLevelPropertyName, - actualLogLevel.toString.toLowerCase() - ) - System.setProperty(appenderPropertyName, appender) - val result = logConfiguration() - .map(conf => - Boolean.unbox( - LoggerContextSetup - .setup(actualLogLevel, logComponentName, conf) - ) - ) - .getOrElse(false) - if (!result) { - System.err.println("Failed to set Logger Context1") - } - loggingServiceEndpointPromise.success(Some(uri)) - } - } else { - System.setProperty("logging-server.host", host) - System.setProperty("logging-server.port", port.toString) - System.setProperty( - logLevelPropertyName, - actualLogLevel.toString.toLowerCase() - ) - System.setProperty(appenderPropertyName, "forward-via-socket") - val result = logConfiguration() - .map(conf => - Boolean.unbox( - LoggerContextSetup - .setup(actualLogLevel, logComponentName, conf) - ) - ) - .getOrElse(false) - if (!result) { - System.err.println("Failed to set Logger Context2") - } - } - Thread.sleep(1000) - } - - def waitForSetup(): Unit = { - Await.ready(loggingServiceEndpointPromise.future, 5.seconds) - } - - def tearDown(): Unit = { - LoggingServiceManager.teardown() - } -} diff --git a/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala b/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala deleted file mode 100644 index ed24e2aacea0..000000000000 --- a/lib/scala/logging-utils/src/main/scala/org/enso/logger/ColorMode.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.enso.logger - -/** Describes possible modes of color display in console output. */ -sealed trait ColorMode -object ColorMode { - - /** Never use color escape sequences in the output. */ - case object Never extends ColorMode - - /** Enable color output if it seems to be supported. */ - case object Auto extends ColorMode - - /** Always use escape sequences in the output, even if the program thinks they - * are unsupported. - * - * May be useful if output is piped to other programs that know how to handle - * the escape sequences. - */ - case object Always extends ColorMode -} diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index 0eea1c84b0b5..c3bd8c005f03 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -1,3 +1,5 @@ +## Project Manager's application.conf + akka { actor.debug.lifecycle = on http { @@ -16,14 +18,38 @@ logging-service { akka.actor = info akka.event = error akka.io = error + akka.http = warn akka.stream = error akka.routing = error } - appender = "forward-via-socket" + appenders = [ + { + name = "socket" + hostname = ${?LOGSERVER_HOSTNAME} + hostname = "localhost" + port = ${?LOGSERVER_PORT} + port = 6000 + }, + { + name = "file", + }, + { + name = "console" + } + ] + defaultAppender = ${?DEFAULT_LOGGER} + defaultAppender = socket server { + start = ${?LOGSERVER_START} + start = true + hostname = ${?LOGSERVER_HOSTNAME} hostname = "localhost" + port = ${?LOGSERVER_PORT} port = 6000 - mode = "file" # file/console/socket/sentry + appender { + name = ${?LOGSERVER_APPENDER} + name = "file" # file/console/socket/sentry + } } } diff --git a/lib/scala/project-manager/src/main/resources/project-manager.logback.xml b/lib/scala/project-manager/src/main/resources/project-manager.logback.xml deleted file mode 100644 index dc31b6d8512d..000000000000 --- a/lib/scala/project-manager/src/main/resources/project-manager.logback.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - ${project-manager.logRoot}/${project-manager.logPrefix}-%d{yyyy-MM-dd}.log - false - true - - [%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n - - - - - ${logging-server.host} - ${logging-server.port} - 10000 - true - - - - - - diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala index f64f29d13a8e..93985c3fe9bd 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala @@ -8,11 +8,11 @@ import org.slf4j.event.Level import java.net.URI import scala.concurrent.Future -import org.enso.logging.LoggingCollectorHelper +import org.enso.logging.LoggingSetupHelper import scala.concurrent.ExecutionContext.Implicits.global /** A helper for setting up the logging service in the Project Manager. */ -object Logging extends LoggingCollectorHelper { +object Logging extends LoggingSetupHelper { /** @inheritdoc */ override val defaultLogLevel: Level = Level.INFO @@ -24,9 +24,6 @@ object Logging extends LoggingCollectorHelper { /** @inheritdoc */ override val logFileSuffix: String = "enso-project-manager" - override def logComponentName: String = "project-manager" - //override def defaultAppenderName: Option[String] = Some("forward-via-socket") - object GlobalLoggingService extends LoggingServiceDescriptor { /** @inheritdoc */ 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 affce7ac479f..02ef94bd6fc6 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 @@ -278,7 +278,8 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ZIO .attempt { - Logging.setup(Some(level), None, logMasking, profilingLog) + Logging.initLogger(Some(level)) + Logging.setupServerAndForwardLogs(Some(level), logMasking, profilingLog) () } .catchAll { exception => diff --git a/lib/scala/project-manager/src/test/resources/application.conf b/lib/scala/project-manager/src/test/resources/application.conf index a2cb10e49091..51e94e0864b6 100644 --- a/lib/scala/project-manager/src/test/resources/application.conf +++ b/lib/scala/project-manager/src/test/resources/application.conf @@ -5,7 +5,20 @@ akka.loglevel = "ERROR" akka.test.timefactor = ${?CI_TEST_TIMEFACTOR} akka.test.single-expect-default = 5s -logging-service.test-log-level = warning +logging-service { + logger { + akka = error + } + appenders = [ + { + name = "console" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" + } + ] + defaultAppender = console + log-level = "warn" +} + project-manager { diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala index f0e74b8a227c..793ed9cd1384 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala @@ -16,8 +16,7 @@ import org.enso.editions.Editions import org.enso.cli.OS import org.enso.jsonrpc.test.JsonRpcServerTestKit import org.enso.jsonrpc.{ClientControllerFactory, ProtocolFactory} -//import org.enso.loggingservice.printers.StderrPrinterWithColors -//import org.enso.loggingservice.{LogLevel, LoggerMode, LoggingServiceManager} +import org.enso.logger.LoggerSetup import org.enso.pkg.{Config, PackageManager} import org.enso.projectmanager.boot.Globals.{ConfigFilename, ConfigNamespace} import org.enso.projectmanager.boot.configuration._ @@ -237,18 +236,14 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { override def beforeAll(): Unit = { super.beforeAll() - setupEditions() - if (debugLogs) { - // TODO - /*LoggingServiceManager.setup( - LoggerMode.Local( - Seq(StderrPrinterWithColors.colorPrinterIfAvailable(true)) - ), - LogLevel.Trace - )*/ + LoggerSetup.setup(Level.TRACE) + } else { + LoggerSetup.setup() } + setupEditions() + engineToInstall.foreach(preInstallEngine) } diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala index cc82f4aad2b0..a224b4d1cd9b 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala @@ -3,13 +3,11 @@ package org.enso.projectmanager.infrastructure.languageserver import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{ImplicitSender, TestActor, TestKit, TestProbe} import com.miguno.akka.testing.VirtualTime +import org.enso.logger.LoggerSetup import org.enso.projectmanager.boot.configuration.SupervisionConfig import org.enso.projectmanager.infrastructure.http.AkkaBasedWebSocketConnectionFactory import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootLoader.ServerBooted -import org.enso.projectmanager.infrastructure.languageserver.ProgrammableWebSocketServer.{ - Reject, - ReplyWith -} +import org.enso.projectmanager.infrastructure.languageserver.ProgrammableWebSocketServer.{Reject, ReplyWith} import org.enso.projectmanager.infrastructure.languageserver.StepParent.ChildTerminated import org.enso.projectmanager.infrastructure.net.Tcp import org.enso.testkit.FlakySpec @@ -106,6 +104,8 @@ class LanguageServerSupervisorSpec trait TestCtx { + LoggerSetup.setup() + val VerificationTimeout = 120000 val virtualTime = new VirtualTime diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/releases/testing/FakeReleaseProvider.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/releases/testing/FakeReleaseProvider.scala index 645705d42800..96df4cff4e42 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/releases/testing/FakeReleaseProvider.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/releases/testing/FakeReleaseProvider.scala @@ -1,6 +1,12 @@ package org.enso.runtimeversionmanager.releases.testing -import java.nio.file.{Files, Path, StandardCopyOption} +import java.nio.file.{ + FileAlreadyExistsException, + Files, + NoSuchFileException, + Path, + StandardCopyOption +} import org.enso.cli.task.{ProgressListener, TaskProgress} import org.enso.distribution.FileSystem import org.enso.distribution.locking.{LockManager, LockType} @@ -147,11 +153,16 @@ case class FakeAsset( } for (sourceToCopy <- pathsToCopy) { - Files.copy( - sourceToCopy, - innerRoot.resolve(sourceToCopy.getFileName), - StandardCopyOption.REPLACE_EXISTING - ) + try { + Files.copy( + sourceToCopy, + innerRoot.resolve(sourceToCopy.getFileName), + StandardCopyOption.REPLACE_EXISTING + ) + } catch { + case _: FileAlreadyExistsException => + case _: NoSuchFileException => + } } TestArchivePackager.packArchive(source, destination) } From 57ff421ba570fc8a501668ad4d68bffd905b67f0 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 21 Aug 2023 20:41:31 +0200 Subject: [PATCH 12/31] More simplifications, documentation --- build.sbt | 3 +- .../src/main/resources/application.conf | 4 +- .../launcher/cli/LauncherApplication.scala | 5 +- .../enso/launcher/cli/LauncherLogging.scala | 6 +- .../scala/org/enso/launcher/cli/Main.scala | 5 +- .../scala/org/enso/runner/RunnerLogging.scala | 51 ++++----- .../config/GlobalConfigurationManager.scala | 2 +- .../java/org/enso/logger/config/Appender.java | 23 ++++ .../org/enso/logger/config/AppenderSetup.java | 17 +++ .../enso/logger/config/ConsoleAppender.java | 21 ++++ .../org/enso/logger/config/FileAppender.java | 16 +++ .../{Loggers.java => LoggersLevels.java} | 41 ++++--- .../{Server.java => LoggingServer.java} | 6 +- .../logger/config/LoggingServiceConfig.java | 20 ++-- .../enso/logger/config/SentryAppender.java | 6 ++ .../enso/logger/config/SocketAppender.java | 20 +++- .../org/enso/logger/ApplicationFilter.java | 8 +- .../java/org/enso/logger/LoggerSetup.java | 102 +++++++++++++----- .../logging/LoggerInitializationFailed.java | 6 +- ...orwardToServer.java => LoggingServer.java} | 6 +- .../logging/LoggingServiceAlreadySetup.java | 7 ++ .../enso/logging/LoggingServiceManager.scala | 16 +-- .../org/enso/logging/LoggingSetupHelper.scala | 101 +++++++++-------- .../projectmanager/boot/ProjectManager.scala | 24 ++--- 24 files changed, 324 insertions(+), 192 deletions(-) create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java rename lib/scala/logging-config/src/main/java/org/enso/logger/config/{Loggers.java => LoggersLevels.java} (64%) rename lib/scala/logging-config/src/main/java/org/enso/logger/config/{Server.java => LoggingServer.java} (58%) rename lib/scala/logging-server/src/main/java/org/enso/logging/{ForwardToServer.java => LoggingServer.java} (88%) create mode 100644 lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java diff --git a/build.sbt b/build.sbt index a04fe6591c37..bb97c92b2390 100644 --- a/build.sbt +++ b/build.sbt @@ -705,6 +705,7 @@ lazy val `logging-server` = project ) ) .dependsOn(`logging-logback`) + .dependsOn(`logging-utils`) lazy val `logging-jutil` = project .in(file("lib/scala/logging-jutil")) @@ -716,7 +717,6 @@ lazy val `logging-jutil` = project "org.slf4j" % "slf4j-api" % slf4jVersion ) ) - .dependsOn(`logging-logback`) lazy val `logging-config` = project .in(file("lib/scala/logging-config")) @@ -741,6 +741,7 @@ lazy val `logging-logback` = project "ch.qos.logback" % "logback-classic" % logbackClassicVersion, "ch.qos.logback" % "logback-core" % logbackClassicVersion, "com.typesafe" % "config" % typesafeConfigVersion, + "io.sentry" % "sentry-logback" % "6.28.0" % Provided, akkaHttp ) ) diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index 813ab53bce3d..a88213d4f4cf 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -23,11 +23,11 @@ logging-service { }, { name = "file", - pattern = null + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n" }, { name = "console" - pattern = null + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] defaultAppender = ${?DEFAULT_LOGGER} diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index 1a7fba78c3c3..58d7e1de27e5 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -654,7 +654,7 @@ object LauncherApplication { Launcher.ensurePortable() } - LauncherLogging.initLogger(logLevel) + LauncherLogging.initLogger() val globalCLIOptions = cli.GlobalCLIOptions( autoConfirm = autoConfirm, @@ -672,8 +672,7 @@ object LauncherApplication { LauncherLogging.setup( logLevel, connectLogger, - !disableLogMasking, - None + !disableLogMasking ) LauncherLogging.waitForSetup() initializeApp() diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 2cf4fd081e81..6723dc0b3b26 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -2,11 +2,9 @@ package org.enso.launcher.cli import java.nio.file.Path import org.enso.launcher.distribution.DefaultManagers +import org.enso.logger.LoggerSetup import org.slf4j.event.Level - import org.enso.logging.LoggingSetupHelper -import org.enso.logging.LoggingServiceManager - import scala.concurrent.ExecutionContext.Implicits.global /** Manages setting up the logging service within the launcher. @@ -35,6 +33,6 @@ object LauncherLogging extends LoggingSetupHelper { def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LoggingServiceManager.fallbackToLocalConsole(actualLogLevel) + LoggerSetup.get().setupConsoleAppender(actualLogLevel) } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index 0aaa11d2f831..bfd29e46d29d 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -1,7 +1,6 @@ package org.enso.launcher.cli import com.typesafe.scalalogging.Logger -import org.enso.logger.LoggerSetup import org.enso.cli.CLIOutput import org.enso.launcher.distribution.DefaultManagers import org.enso.launcher.upgrade.LauncherUpgrader @@ -10,13 +9,13 @@ import org.enso.launcher.upgrade.LauncherUpgrader */ object Main { private def initLogger(): Unit = { - LoggerSetup.setupNoOpAppender() + LauncherLogging.initLogger() } private def runAppHandlingParseErrors(args: Array[String]): Int = LauncherApplication.application.run(args) match { case Left(errors) => - //TODO: LauncherLogging.setupFallback() + LauncherLogging.prepareForUninstall(None) CLIOutput.println(errors.mkString("\n")) 1 case Right(exitCode) => diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index 5b715775518c..043c764056db 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -4,18 +4,18 @@ import java.net.URI import com.typesafe.scalalogging.Logger import org.enso.logger.masking.Masking -import scala.annotation.unused import org.slf4j.event.Level import scala.util.{Failure, Success} import scala.concurrent.Future import org.enso.logger.LoggerSetup -import org.enso.logger.config.LoggingServiceConfig /** Manages setting up the logging service within the runner. */ object RunnerLogging { + private val logger = Logger[RunnerLogging.type] + /** Sets up the runner's logging service. * * If `connectionUri` is provided it tries to connect to a logging service @@ -33,55 +33,44 @@ object RunnerLogging { ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) - val loggingConfig = LoggingServiceConfig.parseConfig() - val loggerSetup = connectionUri match { + val loggerSetup = LoggerSetup.get() + val initializedLogger = connectionUri match { case Some(uri) => Future { - LoggerSetup.setupSocketAppender( + loggerSetup.setupSocketAppender( logLevel, uri.getHost(), - uri.getPort(), - loggingConfig + uri.getPort() ) } .map(success => - if (success) () - else throw new RuntimeException("setup failed") + if (success) { + logger.trace("Connected to logging service at [{}].", uri) + true + } else + throw new RuntimeException("Failed to connect to logging service") ) - .map { _ => - logger.trace("Connected to logging service at [{}].", uri) - } - .recoverWith { _ => + .recoverWith[Boolean] { _ => System.err.println( "Failed to connect to the logging service server, " + "falling back to local logging." ) - setupLocalLogger(logLevel, loggingConfig) + Future.successful(loggerSetup.setupConsoleAppender(logLevel)) } case None => - setupLocalLogger(logLevel, loggingConfig) + Future.successful(loggerSetup.setupConsoleAppender(logLevel)) } - loggerSetup.onComplete { + initializedLogger.onComplete { case Failure(exception) => exception.printStackTrace() - case Success(_) => + System.err.println("Logger setup: " + exception.getMessage) + case Success(success) => + if (!success) { + System.err.println("Failed to initialize logging infrastructure") + } } } - private def setupLocalLogger( - @unused logLevel: Level, - config: LoggingServiceConfig - ): Future[Unit] = { - Future.successful( - LoggerSetup.setupConsoleAppender( - logLevel, - config - ) - ) - } - - private val logger = Logger[RunnerLogging.type] - /** Shuts down the logging service gracefully. */ def tearDown(): Unit = { diff --git a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala index c9618af37678..54915ca22d16 100644 --- a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala +++ b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala @@ -2,7 +2,7 @@ package org.enso.distribution.config import com.typesafe.scalalogging.Logger import io.circe.syntax._ -import io.circe.{Json, yaml} +import io.circe.{yaml, Json} import org.enso.distribution.DistributionManager import org.enso.distribution.FileSystem.PathSyntax diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java index fbf9247f5e5d..49452a1c3e9f 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -1,6 +1,9 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import java.net.URI; +import java.nio.file.Path; +import org.slf4j.event.Level; public abstract class Appender { @@ -19,6 +22,8 @@ public static Appender parse(Config config) { return FileAppender.parse(config); case "socket": return SocketAppender.parse(config); + case "sentry": + return SentryAppender.parse(config); default: return ConsoleAppender.parse(config); } @@ -26,6 +31,24 @@ public static Appender parse(Config config) { return null; } + public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + return false; + } + + public Boolean setupForPath( + Level logLevel, Path logRoot, String logPrefix, AppenderSetup appenderSetup) { + return false; + } + + public Boolean setupForURI( + Level logLevel, String hostname, int port, AppenderSetup appenderSetup) { + return false; + } + + public boolean isSameTargetAs(URI uri) { + return false; + } + public Config getConfig() { return this.raw; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java new file mode 100644 index 000000000000..27f8742845e3 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java @@ -0,0 +1,17 @@ +package org.enso.logger.config; + +import java.nio.file.Path; +import org.slf4j.event.Level; + +public abstract class AppenderSetup { + + public abstract boolean setupSocketAppender(Level logLevel, String hostname, int port); + + public abstract boolean setupFileAppender(Level logLevel, Path logRoot, String logPrefix); + + public abstract boolean setupConsoleAppender(Level logLevel); + + public abstract boolean setupSentryAppender(Level logLevel, String dsn); + + public abstract boolean setupNoOpAppender(); +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java index 060fb25466a8..5e53b1713f83 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java @@ -1,6 +1,8 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import java.nio.file.Path; +import org.slf4j.event.Level; public class ConsoleAppender extends Appender { @@ -16,6 +18,25 @@ public static ConsoleAppender parse(Config config) { return new ConsoleAppender(pattern, config); } + @Override + public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + return appenderSetup.setupConsoleAppender(logLevel); + } + + @Override + public Boolean setupForPath( + Level logLevel, + Path componentLogPath, + String componentLogPrefix, + AppenderSetup appenderSetup) { + return appenderSetup.setupConsoleAppender(logLevel); + } + + @Override + public Boolean setupForURI(Level logLevel, String host, int port, AppenderSetup appenderSetup) { + return appenderSetup.setupConsoleAppender(logLevel); + } + public String getPattern() { return pattern; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java index bcb072cc2720..a690af4a77ee 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -1,6 +1,8 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import java.nio.file.Path; +import org.slf4j.event.Level; public class FileAppender extends Appender { @@ -29,6 +31,20 @@ public static Appender parse(Config config) { return new FileAppender(append, immediateFlush, pattern, config); } + @Override + public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + return appenderSetup.setupFileAppender(logLevel, null, null); + } + + @Override + public Boolean setupForPath( + Level logLevel, + Path componentLogPath, + String componentLogPrefix, + AppenderSetup appenderSetup) { + return appenderSetup.setupFileAppender(logLevel, componentLogPath, componentLogPrefix); + } + @Override public String getName() { return name; diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java similarity index 64% rename from lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java rename to lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java index 75312b92f9e0..e58938e53673 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Loggers.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java @@ -7,11 +7,11 @@ import java.util.stream.Collectors; import org.slf4j.event.Level; -public class Loggers { +public class LoggersLevels { private Map loggers; - private Loggers(Map loggers) { + private LoggersLevels(Map loggers) { this.loggers = loggers; } @@ -19,7 +19,7 @@ public Set> entrySet() { return loggers.entrySet(); } - public static Loggers parse(Config config) { + public static LoggersLevels parse(Config config) { // LinkedHashMap ensures that wildcard loggers are de-prioritized Map loggers = systemLoggers(); Map fallbacks = new LinkedHashMap<>(); @@ -42,24 +42,32 @@ public static Loggers parse(Config config) { if (!fallbacks.isEmpty()) { loggers.putAll(fallbacks); } - return new Loggers(loggers); + return new LoggersLevels(loggers); } + /** + * Read any loggers' levels set via `-Dfoo.bar.Logger.level=` env variables. + * + * @return a map of custom loggers' levels set on startup + */ private static Map systemLoggers() { - Map loggers = new LinkedHashMap<>(); - System.getProperties().forEach((keyObj, value) -> { - String key = keyObj.toString(); - if (key.endsWith(SYS_PROP_SUFFIX)) { - int idx = key.lastIndexOf(SYS_PROP_SUFFIX); - String loggerName = key.substring(0, idx); - try { + Map loggers = new LinkedHashMap<>(); + System.getProperties() + .forEach( + (keyObj, value) -> { + String key = keyObj.toString(); + if (key.endsWith(SYS_PROP_SUFFIX)) { + int idx = key.lastIndexOf(SYS_PROP_SUFFIX); + String loggerName = key.substring(0, idx); + try { loggers.put(loggerName, Level.valueOf(value.toString().toUpperCase())); - } catch (IllegalArgumentException e) { - System.err.println("Invalid log level `" + value + "` for " + loggerName + ". Skipping..."); + } catch (IllegalArgumentException e) { + System.err.println( + "Invalid log level `" + value + "` for " + loggerName + ". Skipping..."); + } } - } - }); - return loggers; + }); + return loggers; } @Override @@ -74,5 +82,6 @@ public java.lang.String toString() { private static String normalizeKey(String key) { return key.replace("'", "").replace("\"", ""); } + private static String SYS_PROP_SUFFIX = ".Logger.level"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java similarity index 58% rename from lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java rename to lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java index f0e29277bdac..3a520c2dab75 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Server.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java @@ -2,14 +2,14 @@ import com.typesafe.config.Config; -public record Server(String hostname, int port, Appender appender, Boolean start) { +public record LoggingServer(String hostname, int port, Appender appender, Boolean start) { - public static Server parse(Config config) { + public static LoggingServer parse(Config config) { int port = config.getInt("port"); String hostname = config.getString("hostname"); Appender appender = Appender.parse(config.getConfig("appender")); boolean start = config.getBoolean("start"); - return new Server(hostname, port, appender, start); + return new LoggingServer(hostname, port, appender, start); } } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java index 9f6e23c21e61..ec84bf8be1b3 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java @@ -16,19 +16,19 @@ public class LoggingServiceConfig { public static final String defaultAppenderKey = "defaultAppender"; public static final String logLevelKey = "log-level"; - private final Loggers loggers; + private final LoggersLevels loggers; private final Map appenders; private final String defaultAppenderName; private final Optional logLevel; - private final Server server; + private final LoggingServer server; private LoggingServiceConfig( - Loggers loggers, + LoggersLevels loggers, Optional logLevel, Map appenders, String defaultAppender, - Server server) { + LoggingServer server) { this.loggers = loggers; this.appenders = appenders; this.defaultAppenderName = defaultAppender; @@ -39,10 +39,10 @@ private LoggingServiceConfig( public static LoggingServiceConfig parseConfig() { var empty = ConfigFactory.empty().atKey(configurationRoot); var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); - Server server; + LoggingServer server; if (root.hasPath(serverKey)) { Config serverConfig = root.getConfig(serverKey); - server = Server.parse(serverConfig); + server = LoggingServer.parse(serverConfig); } else { server = null; } @@ -54,9 +54,9 @@ public static LoggingServiceConfig parseConfig() { appendersMap.put(a.getName(), a); } } - Loggers loggers; + LoggersLevels loggers; if (root.hasPath(loggersKey)) { - loggers = Loggers.parse(root.getConfig(loggersKey)); + loggers = LoggersLevels.parse(root.getConfig(loggersKey)); } else { loggers = null; } @@ -68,7 +68,7 @@ public static LoggingServiceConfig parseConfig() { server); } - public Loggers getLoggers() { + public LoggersLevels getLoggers() { return loggers; } @@ -96,7 +96,7 @@ public Optional getLogLevel() { return logLevel; } - public Server getServer() { + public LoggingServer getServer() { return server; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java index 9062ef2b0d16..281324af5111 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -1,6 +1,7 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import org.slf4j.event.Level; public class SentryAppender extends Appender { private String name; @@ -16,6 +17,11 @@ public static Appender parse(Config config) { return new SentryAppender(config.getString("dsn"), config); } + @Override + public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + return appenderSetup.setupSentryAppender(logLevel, dsn); + } + @Override public String getName() { return name; diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java index 4100531532b7..fc14be8892c7 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -2,6 +2,7 @@ import com.typesafe.config.Config; import java.net.URI; +import org.slf4j.event.Level; public class SocketAppender extends Appender { private String name; @@ -33,11 +34,22 @@ public String getName() { return name; } - public boolean isSameTarget(URI uri) { - return uri.getHost().equals(hostname) && uri.getPort() == port; - } - public static Appender parse(Config config) { return new SocketAppender(config.getString("hostname"), config.getInt("port"), config); } + + @Override + public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + return appenderSetup.setupSocketAppender(logLevel, hostname, port); + } + + @Override + public Boolean setupForURI(Level logLevel, String host, int port, AppenderSetup appenderSetup) { + return appenderSetup.setupSocketAppender(logLevel, host, port); + } + + @Override + public boolean isSameTargetAs(URI uri) { + return uri.getHost().equals(hostname) && uri.getPort() == port; + } } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java index 2dec87138ecb..fe61f5eaf248 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java @@ -4,12 +4,12 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.spi.FilterReply; -import org.enso.logger.config.Loggers; +import org.enso.logger.config.LoggersLevels; public class ApplicationFilter extends Filter { - private final Loggers loggers; + private final LoggersLevels loggers; - private ApplicationFilter(Loggers loggers) { + private ApplicationFilter(LoggersLevels loggers) { this.loggers = loggers; } @@ -28,7 +28,7 @@ public FilterReply decide(ILoggingEvent event) { return FilterReply.NEUTRAL; } - public static Filter fromLoggers(Loggers loggers) { + public static Filter fromLoggers(LoggersLevels loggers) { return new ApplicationFilter(loggers); } } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java index 54308dde7525..21b7590f54ad 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java @@ -10,18 +10,49 @@ import ch.qos.logback.core.filter.Filter; import ch.qos.logback.core.helpers.NOPAppender; +import io.sentry.SentryOptions; +import io.sentry.logback.SentryAppender; + import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Path; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import org.enso.logger.config.AppenderSetup; import org.enso.logger.config.LoggingServiceConfig; import org.enso.logger.config.Appender; -import org.enso.logger.config.Loggers; +import org.enso.logger.config.LoggersLevels; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; -public class LoggerSetup { +public class LoggerSetup extends AppenderSetup { + + private static LoggerSetup _setup = null; + + private final static Object lock = new Object(); + + public LoggingServiceConfig getConfig() { + return config; + } + + private final LoggingServiceConfig config; + + private LoggerSetup(LoggingServiceConfig config) { + this.config = config; + } + + public static LoggerSetup get() { + if (_setup == null) { + synchronized (lock) { + if (_setup == null) { + _setup = new LoggerSetup(LoggingServiceConfig.parseConfig()); + } + } + } + return _setup; + } private static final String defaultPattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n"; @@ -29,40 +60,43 @@ public class LoggerSetup { private static final String socketAppenderKey = "socket"; private static final String consoleAppenderKey = "console"; - public static Boolean setup() { + public Boolean setup() { LoggingServiceConfig config = LoggingServiceConfig.parseConfig(); return setup(config); } - public static Boolean setup(LoggingServiceConfig config) { + private Boolean setup(LoggingServiceConfig config) { Level defaultLogLevel = config.getLogLevel().map(name -> Level.valueOf(name.toUpperCase())).orElseGet(() -> Level.ERROR); return setup(defaultLogLevel, config); } - public static Boolean setup(Level logLevel) { + public Boolean setup(Level logLevel) { return setup(logLevel, LoggingServiceConfig.parseConfig()); } - public static Boolean setup(Level logLevel, LoggingServiceConfig config) { + public Boolean setup(Level logLevel, LoggingServiceConfig config) { Appender defaultAppender = config.getAppender(); if (defaultAppender != null) { - switch (defaultAppender.getName()) { - case fileAppenderKey: - return setupFileAppender(logLevel, null, null, config); - case socketAppenderKey: - return setupSocketAppender(logLevel, defaultAppender.getConfig().getString("hostname"), defaultAppender.getConfig().getInt("port"), config); - default: - return setupConsoleAppender(logLevel, config); - } + return defaultAppender.setup(logLevel, this); } else { - return setupConsoleAppender(logLevel, config); + return setupConsoleAppender(logLevel); } } - public static Boolean setupSocketAppender( + + public Boolean setup(Level logLevel, Path componentLogPath, String componentLogPrefix, LoggingServiceConfig config) { + Appender defaultAppender = config.getAppender(); + if (defaultAppender != null) { + return defaultAppender.setupForPath(logLevel, componentLogPath, componentLogPrefix, this); + } else { + return setupConsoleAppender(logLevel); + } + } + + @Override + public boolean setupSocketAppender( Level logLevel, String hostname, - int port, - LoggingServiceConfig config) { + int port) { LoggerAndContext env = contextInit(logLevel, config); SocketAppender socketAppender = new SocketAppender(); @@ -77,15 +111,14 @@ public static Boolean setupSocketAppender( } - public static Boolean setupFileAppender( + @Override + public boolean setupFileAppender( Level logLevel, Path logRoot, - String logPrefix, - LoggingServiceConfig config - ) { + String logPrefix) { LoggerAndContext env = contextInit(logLevel, config); - String configPattern = ((org.enso.logger.config.ConsoleAppender)config.getAppender(fileAppenderKey)).getPattern(); + String configPattern = ((org.enso.logger.config.FileAppender)config.getAppender(fileAppenderKey)).getPattern(); final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setPattern(configPattern == null ? defaultPattern : configPattern); env.finalizeEncoder(encoder); @@ -109,7 +142,8 @@ public static Boolean setupFileAppender( return true; } - public static Boolean setupConsoleAppender(Level logLevel, LoggingServiceConfig config) { + @Override + public boolean setupConsoleAppender(Level logLevel) { LoggerAndContext env = contextInit(logLevel, config); String consolePattern; @@ -130,7 +164,21 @@ public static Boolean setupConsoleAppender(Level logLevel, LoggingServiceConfig return true; } - public static Boolean setupNoOpAppender() { + @Override + public boolean setupSentryAppender(Level logLevel, String dsn) { + LoggerAndContext env = contextInit(logLevel, config); + + SentryAppender appender = new SentryAppender(); + SentryOptions opts = new SentryOptions(); + opts.setDsn(dsn); + appender.setOptions(opts); + + env.finalizeAppender(appender); + return true; + } + + @Override + public boolean setupNoOpAppender() { LoggerAndContext env = contextInit(Level.ERROR, null); NOPAppender appender = new NOPAppender<>(); @@ -146,14 +194,14 @@ public static void teardown() { context.stop(); } - private static LoggerAndContext contextInit(Level level, LoggingServiceConfig config) { + private LoggerAndContext contextInit(Level level, LoggingServiceConfig config) { LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); context.reset(); context.setName("enso-custom"); Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); Filter filter; - Loggers loggers = config != null ? config.getLoggers() : null; + LoggersLevels loggers = config != null ? config.getLoggers() : null; if (loggers != null) { filter = ApplicationFilter.fromLoggers(loggers); } else { diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java index 84670389dea3..94da192c6f74 100644 --- a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java @@ -1,7 +1,7 @@ package org.enso.logging; public class LoggerInitializationFailed extends RuntimeException { - public LoggerInitializationFailed() { - super("Logger initialization failed"); - } + public LoggerInitializationFailed() { + super("Logger initialization failed"); + } } diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java similarity index 88% rename from lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java rename to lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java index a68a4722a368..fdecdaaf43ab 100644 --- a/lib/scala/logging-server/src/main/java/org/enso/logging/ForwardToServer.java +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java @@ -9,17 +9,17 @@ import java.nio.file.Path; import org.slf4j.event.Level; -class ForwardToServer extends LoggingService { +class LoggingServer extends LoggingService { private int port; private SimpleSocketServer logServer; - public ForwardToServer(int port) { + public LoggingServer(int port) { this.port = port; this.logServer = null; } - public URI logToFile(Level level, Path path, String prefix, String appenderName) + public URI start(Level level, Path path, String prefix, String appenderName) throws URISyntaxException, JoranException { var lc = new LoggerContext(); var configurator = new JoranConfigurator(); diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java new file mode 100644 index 000000000000..159b8191e012 --- /dev/null +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java @@ -0,0 +1,7 @@ +package org.enso.logging; + +public class LoggingServiceAlreadySetup extends RuntimeException { + public LoggingServiceAlreadySetup() { + super("Logging Service already setup"); + } +} diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala index acea30447a15..c5d72af9a1a2 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -1,7 +1,5 @@ package org.enso.logging -import org.enso.logger.LoggerSetup - import java.net.URI import org.slf4j.event.Level @@ -24,25 +22,17 @@ object LoggingServiceManager { appenderName: String )(implicit ec: ExecutionContext): Future[URI] = { if (loggingService != null) { - throw new RuntimeException("logging service already setup") + throw new LoggingServiceAlreadySetup() } else { currentLevel = logLevel - val forwarder = new ForwardToServer(port) + val forwarder = new LoggingServer(port) loggingService = forwarder Future { - forwarder.logToFile(logLevel, logPath, logFileSuffix, appenderName) + forwarder.start(logLevel, logPath, logFileSuffix, appenderName) } } } - def fallbackToLocalConsole(logLevel: Level): Unit = { - if (loggingService != null) { - loggingService.teardown() - } - LoggerSetup.setup(logLevel) - loggingService = null; - } - def teardown(): Unit = { if (loggingService != null) { loggingService.teardown() diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala index 3b84a97df7d8..77cb03f90b52 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala @@ -4,13 +4,12 @@ import org.slf4j.event.Level import java.net.URI import java.nio.file.Path -import scala.annotation.unused import scala.concurrent.Future import scala.concurrent.ExecutionContext import scala.concurrent.Promise import scala.util.{Failure, Success} import org.enso.logger.LoggerSetup -import org.enso.logger.config.{LoggingServiceConfig, SocketAppender} +import org.enso.logger.masking.Masking import scala.concurrent.Await import scala.concurrent.duration.DurationInt @@ -27,31 +26,44 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { private val loggingServiceEndpointPromise = Promise[Option[URI]]() - def initLogger(logLevel: Option[Level]): Unit = { - LoggerSetup.setupConsoleAppender(logLevel.getOrElse(Level.ERROR), null); + /** Initialize logging to console prior to establishing logging. + * Some logs may be added while inferring the parameters of logging infrastructure, leading to catch-22 situations. + */ + def initLogger(): Unit = { + LoggerSetup.get().setupNoOpAppender() } + /** Starts a logging server that collects logs from different components and immediate sets up logs from this component + * to send logs to it. + * @param logLevel + * @param logMasking + */ def setupServerAndForwardLogs( logLevel: Option[Level], - logMasking: Boolean, - profilingLog: Option[Path] + logMasking: Boolean ): Unit = { - // Setup server at a given port - val config = LoggingServiceConfig.parseConfig() + val loggerSetup = LoggerSetup.get() + val config = loggerSetup.getConfig if (config.loggingServerNeedsBoot()) { val actualPort = config.getServer().port(); val actualLogLevel = logLevel.getOrElse(defaultLogLevel) LoggingServiceManager - .setupServer(actualLogLevel, actualPort, logPath, logFileSuffix, config.getServer().appender().getName()) + .setupServer( + actualLogLevel, + actualPort, + logPath, + logFileSuffix, + config.getServer().appender().getName() + ) .onComplete { - // fallback to whatever is the default appender - case Failure(exception) => - exception.printStackTrace() - setup(logLevel, None, logMasking, profilingLog, config) + case Failure(_) => + // fallback to the default appender + setup(logLevel, None, logMasking, loggerSetup) case Success(uri) => - val result = LoggerSetup.setup(actualLogLevel, config) - + Masking.setup(logMasking) + val result = loggerSetup.setup(actualLogLevel) if (!result) { + LoggingServiceManager.teardown() loggingServiceEndpointPromise.failure( new LoggerInitializationFailed() ) @@ -60,9 +72,9 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { } } } else { - // just setup whatever is the default + // Setup logger according to config val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - if (LoggerSetup.setup(actualLogLevel, config)) { + if (loggerSetup.setup(actualLogLevel)) { loggingServiceEndpointPromise.success(None) } } @@ -71,65 +83,62 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { def setup( logLevel: Option[Level], connectToExternalLogger: Option[URI], - logMasking: Boolean, - profilingLog: Option[Path] + logMasking: Boolean ): Unit = { setup( logLevel, connectToExternalLogger, logMasking, - profilingLog, - LoggingServiceConfig.parseConfig() + LoggerSetup.get() ); } def setup( logLevel: Option[Level], connectToExternalLogger: Option[URI], - @unused logMasking: Boolean, - @unused profilingLog: Option[Path], - config: LoggingServiceConfig + logMasking: Boolean, + loggerSetup: LoggerSetup ): Unit = { val actualLogLevel = logLevel.getOrElse(defaultLogLevel) connectToExternalLogger match { case Some(uri) => - var initialized = LoggerSetup.setupSocketAppender( + var initialized = loggerSetup.setupSocketAppender( actualLogLevel, uri.getHost(), - uri.getPort(), - config + uri.getPort() ) if (!initialized) { // Fallback, try the default appender if it is not the socket appender to the same port - config.getAppender() match { - case defaultAppender: SocketAppender => - if (!defaultAppender.isSameTarget(uri)) { - initialized = LoggerSetup.setup(actualLogLevel, config) - if (!initialized) { - // Fallback to console - initialized = - LoggerSetup.setupConsoleAppender(actualLogLevel, config); - } - } else { - // Fallback to console - initialized = - LoggerSetup.setupConsoleAppender(actualLogLevel, config); - } - case _ => - initialized = LoggerSetup.setup(actualLogLevel, config) + val defaultAppender = loggerSetup.getConfig().getAppender() + if (!defaultAppender.isSameTargetAs(uri)) { + initialized = loggerSetup.setup(actualLogLevel) + if (!initialized) { + // Fallback to console + initialized = loggerSetup.setupConsoleAppender(actualLogLevel) + } + } else { + // Fallback to console + initialized = loggerSetup.setupConsoleAppender(actualLogLevel) } } + if (initialized) { + Masking.setup(logMasking) loggingServiceEndpointPromise.success(None) } else { - loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()) + loggingServiceEndpointPromise.failure( + new LoggerInitializationFailed() + ) } case None => - if (LoggerSetup.setup(actualLogLevel, config)) { + if (loggerSetup.setup(actualLogLevel)) { + Masking.setup(logMasking) loggingServiceEndpointPromise.success(None) } else { - loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()) + loggingServiceEndpointPromise.failure( + new LoggerInitializationFailed() + ) } } } 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 02ef94bd6fc6..7a0c8db2a0be 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 @@ -4,8 +4,6 @@ import akka.http.scaladsl.Http import com.typesafe.scalalogging.LazyLogging import org.apache.commons.cli.CommandLine -import scala.annotation.unused -//import org.enso.loggingservice.{ColorMode, LogLevel} import org.enso.projectmanager.boot.Globals.{ ConfigFilename, ConfigNamespace, @@ -25,7 +23,7 @@ import zio.interop.catz.core._ import zio.{ExitCode, Runtime, Scope, UIO, ZAny, ZIO, ZIOAppArgs, ZIOAppDefault} import java.io.{EOFException, IOException} -import java.nio.file.{FileAlreadyExistsException, Files, Path, Paths} +import java.nio.file.{FileAlreadyExistsException, Files, Paths} import java.util.concurrent.ScheduledThreadPoolExecutor import scala.concurrent.duration._ @@ -241,9 +239,8 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { makeVersionDescription.asString(useJson = false) ) for { - opts <- parseOpts(options) - profilingLog = opts.profilingPath.map(getSiblingFile(_, ".log")) - logLevel <- setupLogging(verbosity, logMasking, profilingLog) + opts <- parseOpts(options) + logLevel <- setupLogging(verbosity, logMasking) procConf = MainProcessConfig( logLevel, opts.profilingRuntimeEventsLog, @@ -263,8 +260,7 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { private def setupLogging( verbosityLevel: Int, - @unused logMasking: Boolean, - @unused profilingLog: Option[Path] + logMasking: Boolean ): ZIO[ZAny, IOException, Level] = { val level = verbosityLevel match { case 0 => Level.INFO @@ -278,8 +274,8 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ZIO .attempt { - Logging.initLogger(Some(level)) - Logging.setupServerAndForwardLogs(Some(level), logMasking, profilingLog) + Logging.initLogger() + Logging.setupServerAndForwardLogs(Some(level), logMasking) () } .catchAll { exception => @@ -321,12 +317,4 @@ object ProjectManager extends ZIOAppDefault 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) - } } From bece946120a279f35372c011a504a0e6bbbb3e5f Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 22 Aug 2023 22:53:38 +0200 Subject: [PATCH 13/31] Functionality-wise - done. Cleaned up implementation for all appenders. All logback configs are gone, everything is done via our application configs. Still missing some documentation but no more features are planned. --- .../src/main/resources/application.conf | 4 +- .../src/test/resources/application.conf | 2 +- .../websocket/json/BaseServerTest.scala | 6 +- .../src/main/resources/application.conf | 4 +- .../enso/launcher/cli/LauncherLogging.scala | 4 +- .../src/test/resources/application.conf | 2 +- .../scala/org/enso/runner/RunnerLogging.scala | 6 +- .../java/org/enso/logger/config/Appender.java | 39 ++- .../org/enso/logger/config/AppenderSetup.java | 17 -- .../enso/logger/config/ConsoleAppender.java | 30 +- .../org/enso/logger/config/FileAppender.java | 112 +++++-- .../org/enso/logger/config/LoggerSetup.java | 31 ++ .../org/enso/logger/config/LoggersLevels.java | 10 + .../org/enso/logger/config/LoggingServer.java | 2 +- .../logger/config/LoggingServiceConfig.java | 29 +- .../config/MissingConfigurationField.java | 7 + .../enso/logger/config/SentryAppender.java | 25 +- .../enso/logger/config/SocketAppender.java | 55 ++-- .../main/java/org/enso/logger/JulHandler.java | 1 + .../java/org/enso/logger/LogbackSetup.java | 289 ++++++++++++++++++ .../java/org/enso/logger/LoggerSetup.java | 231 -------------- .../java/org/enso/logging/LoggingServer.java | 14 +- .../main/resources/logging-server.logback.xml | 29 -- .../enso/logging/LoggingServiceManager.scala | 6 +- .../org/enso/logging/LoggingSetupHelper.scala | 11 +- .../src/main/resources/application.conf | 9 +- .../src/test/resources/application.conf | 2 +- .../enso/projectmanager/BaseServerSpec.scala | 6 +- .../LanguageServerSupervisorSpec.scala | 9 +- 29 files changed, 580 insertions(+), 412 deletions(-) delete mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/MissingConfigurationField.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java delete mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java delete mode 100644 lib/scala/logging-server/src/main/resources/logging-server.logback.xml diff --git a/engine/language-server/src/main/resources/application.conf b/engine/language-server/src/main/resources/application.conf index a41cd38370d7..f06aed348b98 100644 --- a/engine/language-server/src/main/resources/application.conf +++ b/engine/language-server/src/main/resources/application.conf @@ -40,6 +40,6 @@ logging-service { name = "console" } ] - defaultAppender = ${?DEFAULT_LOGGER} - defaultAppender = socket + default-appender = ${?DEFAULT_LOGGER} + default-appender = socket } diff --git a/engine/language-server/src/test/resources/application.conf b/engine/language-server/src/test/resources/application.conf index 58afffe0b834..244c41c38cab 100644 --- a/engine/language-server/src/test/resources/application.conf +++ b/engine/language-server/src/test/resources/application.conf @@ -27,6 +27,6 @@ logging-service { pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] - defaultAppender = console + default-appender = console log-level = "error" } diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala index e9b4842eb631..43d19e17b13f 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala @@ -45,6 +45,7 @@ import org.enso.languageserver.vcsmanager.{Git, VcsManager} import org.enso.librarymanager.LibraryLocations import org.enso.librarymanager.local.DefaultLocalLibraryProvider import org.enso.librarymanager.published.PublishedLibraryCache +import org.enso.logger.LogbackSetup import org.enso.pkg.PackageManager import org.enso.polyglot.data.TypeGraph import org.enso.polyglot.runtime.Runtime.Api @@ -60,12 +61,9 @@ import org.slf4j.event.Level import java.nio.file.{Files, Path} import java.util.UUID - import scala.concurrent.Await import scala.concurrent.duration._ -import org.enso.logger.LoggerSetup - class BaseServerTest extends JsonRpcServerTestKit with EitherValue @@ -77,7 +75,7 @@ class BaseServerTest val timeout: FiniteDuration = 10.seconds - LoggerSetup.setup() + LogbackSetup.get().setup() def isFileWatcherEnabled: Boolean = false diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index a88213d4f4cf..2c265c7882e7 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -30,6 +30,6 @@ logging-service { pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] - defaultAppender = ${?DEFAULT_LOGGER} - defaultAppender = file + default-appender = ${?DEFAULT_LOGGER} + default-appender = file } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 6723dc0b3b26..351e5adb1940 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -2,7 +2,7 @@ package org.enso.launcher.cli import java.nio.file.Path import org.enso.launcher.distribution.DefaultManagers -import org.enso.logger.LoggerSetup +import org.enso.logger.LogbackSetup import org.slf4j.event.Level import org.enso.logging.LoggingSetupHelper import scala.concurrent.ExecutionContext.Implicits.global @@ -33,6 +33,6 @@ object LauncherLogging extends LoggingSetupHelper { def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LoggerSetup.get().setupConsoleAppender(actualLogLevel) + LogbackSetup.get().setupConsoleAppender(actualLogLevel) } } diff --git a/engine/launcher/src/test/resources/application.conf b/engine/launcher/src/test/resources/application.conf index 6091d62e22ad..e6e206cd8021 100644 --- a/engine/launcher/src/test/resources/application.conf +++ b/engine/launcher/src/test/resources/application.conf @@ -7,6 +7,6 @@ logging-service { pattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] - defaultAppender = console + default-appender = console log-level = "warn" } diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index 043c764056db..1297b4d4c046 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -2,13 +2,13 @@ package org.enso.runner import java.net.URI import com.typesafe.scalalogging.Logger +import org.enso.logger.LogbackSetup import org.enso.logger.masking.Masking import org.slf4j.event.Level import scala.util.{Failure, Success} import scala.concurrent.Future -import org.enso.logger.LoggerSetup /** Manages setting up the logging service within the runner. */ @@ -33,7 +33,7 @@ object RunnerLogging { ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) - val loggerSetup = LoggerSetup.get() + val loggerSetup = LogbackSetup.get() val initializedLogger = connectionUri match { case Some(uri) => Future { @@ -74,6 +74,6 @@ object RunnerLogging { /** Shuts down the logging service gracefully. */ def tearDown(): Unit = { - LoggerSetup.teardown() + LogbackSetup.teardown() } } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java index 49452a1c3e9f..14ac650f4f8d 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -5,51 +5,48 @@ import java.nio.file.Path; import org.slf4j.event.Level; +/** Base class for all appenders supported in Enso's config file. */ public abstract class Appender { - private final Config raw; - - public Appender(Config raw) { - this.raw = raw; - } - public abstract String getName(); - public static Appender parse(Config config) { + public static Appender parse(Config config) throws MissingConfigurationField { if (config != null) { - switch (config.getString("name")) { - case "file": + switch (config.getString(nameKey)) { + case FileAppender.appenderName: return FileAppender.parse(config); - case "socket": + case SocketAppender.appenderName: return SocketAppender.parse(config); - case "sentry": + case SentryAppender.appenderName: return SentryAppender.parse(config); - default: + case ConsoleAppender.appenderName: return ConsoleAppender.parse(config); + default: + return null; } } return null; } - public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { + public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { return false; } public Boolean setupForPath( - Level logLevel, Path logRoot, String logPrefix, AppenderSetup appenderSetup) { - return false; + Level logLevel, Path logRoot, String logPrefix, LoggerSetup appenderSetup) { + return setup(logLevel, appenderSetup); } - public Boolean setupForURI( - Level logLevel, String hostname, int port, AppenderSetup appenderSetup) { - return false; + public Boolean setupForURI(Level logLevel, String hostname, int port, LoggerSetup appenderSetup) { + return setup(logLevel, appenderSetup); } public boolean isSameTargetAs(URI uri) { return false; } - public Config getConfig() { - return this.raw; - } + public static final String defaultPattern = + "[%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n"; + protected static final String patternKey = "pattern"; + private static final String nameKey = "name"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java deleted file mode 100644 index 27f8742845e3..000000000000 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/AppenderSetup.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.enso.logger.config; - -import java.nio.file.Path; -import org.slf4j.event.Level; - -public abstract class AppenderSetup { - - public abstract boolean setupSocketAppender(Level logLevel, String hostname, int port); - - public abstract boolean setupFileAppender(Level logLevel, Path logRoot, String logPrefix); - - public abstract boolean setupConsoleAppender(Level logLevel); - - public abstract boolean setupSentryAppender(Level logLevel, String dsn); - - public abstract boolean setupNoOpAppender(); -} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java index 5e53b1713f83..0e69d25a29d8 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java @@ -1,39 +1,25 @@ package org.enso.logger.config; import com.typesafe.config.Config; -import java.nio.file.Path; import org.slf4j.event.Level; +/** Config for log configuration that appends to the console */ public class ConsoleAppender extends Appender { private final String pattern; - private ConsoleAppender(String pattern, Config config) { - super(config); + private ConsoleAppender(String pattern) { this.pattern = pattern; } public static ConsoleAppender parse(Config config) { - String pattern = config.hasPath("pattern") ? config.getString("pattern") : null; - return new ConsoleAppender(pattern, config); + String pattern = + config.hasPath(patternKey) ? config.getString(patternKey) : Appender.defaultPattern; + return new ConsoleAppender(pattern); } @Override - public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { - return appenderSetup.setupConsoleAppender(logLevel); - } - - @Override - public Boolean setupForPath( - Level logLevel, - Path componentLogPath, - String componentLogPrefix, - AppenderSetup appenderSetup) { - return appenderSetup.setupConsoleAppender(logLevel); - } - - @Override - public Boolean setupForURI(Level logLevel, String host, int port, AppenderSetup appenderSetup) { + public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { return appenderSetup.setupConsoleAppender(logLevel); } @@ -43,6 +29,8 @@ public String getPattern() { @Override public String getName() { - return "console"; + return appenderName; } + + public static final String appenderName = "console"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java index a690af4a77ee..e2ac7f07677f 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -2,55 +2,127 @@ import com.typesafe.config.Config; import java.nio.file.Path; +import java.nio.file.Paths; import org.slf4j.event.Level; +/** Config for log configuration that appends to the file. */ public class FileAppender extends Appender { - - private static String immediateFlushKey = "immediate-flush"; - private static String appendKey = "append"; - - private static String patternKey = "pattern"; - private final String name; private final boolean append; private final boolean immediateFlush; private final String pattern; - private FileAppender(boolean append, boolean immediateFlush, String pattern, Config config) { - super(config); - this.name = "file"; + private final LogLocation logLocation; + private final RollingPolicy rollingPolicy; + + private FileAppender( + boolean append, + boolean immediateFlush, + String pattern, + LogLocation logLocation, + RollingPolicy rollingPolicy) { this.append = append; this.immediateFlush = immediateFlush; this.pattern = pattern; + + this.logLocation = logLocation; + this.rollingPolicy = rollingPolicy; } public static Appender parse(Config config) { boolean append = config.hasPath(appendKey) ? config.getBoolean(appendKey) : true; boolean immediateFlush = - config.hasPath(immediateFlushKey) ? config.getBoolean("immediate-flush") : false; - String pattern = config.hasPath(patternKey) ? config.getString(patternKey) : null; - return new FileAppender(append, immediateFlush, pattern, config); + config.hasPath(immediateFlushKey) ? config.getBoolean(immediateFlushKey) : false; + String pattern = + config.hasPath(patternKey) ? config.getString(patternKey) : Appender.defaultPattern; + + LogLocation location; + if (config.hasPath(logLocationKey) + && config.hasPath(logRootKey) + && config.hasPath(logPrefixKey)) { + Config logLocationConfig = config.getConfig(logLocationKey); + location = + new LogLocation( + Paths.get(logLocationConfig.getString(logRootKey)), + logLocationConfig.getString(logPrefixKey)); + } else { + location = new LogLocation(null, null); + } + + RollingPolicy rollingPolicy; + if (config.hasPath(rollingPolicyKey)) { + Config c = config.getConfig(rollingPolicyKey); + rollingPolicy = + new RollingPolicy( + stringWithDefault(c, maxFileSizeKey, "50MB"), + initWithDefault(c, maxHistoryKey, 30), + stringWithDefault(c, maxTotalSizeKey, "2GB")); + } else { + rollingPolicy = null; + } + + return new FileAppender(append, immediateFlush, pattern, location, rollingPolicy); } @Override - public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { - return appenderSetup.setupFileAppender(logLevel, null, null); + public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + return appenderSetup.setupFileAppender( + logLevel, logLocation.logRoot(), logLocation.logPrefix()); } @Override public Boolean setupForPath( - Level logLevel, - Path componentLogPath, - String componentLogPrefix, - AppenderSetup appenderSetup) { - return appenderSetup.setupFileAppender(logLevel, componentLogPath, componentLogPrefix); + Level logLevel, Path componentLogPath, String componentLogPrefix, LoggerSetup loggerSetup) { + return loggerSetup.setupFileAppender(logLevel, componentLogPath, componentLogPrefix); } @Override public String getName() { - return name; + return appenderName; + } + + public boolean isAppend() { + return append; + } + + public boolean isImmediateFlush() { + return immediateFlush; } public String getPattern() { return pattern; } + + public RollingPolicy getRollingPolicy() { + return rollingPolicy; + } + + public record LogLocation(Path logRoot, String logPrefix) {} + + public record RollingPolicy(String maxFileSize, int maxHistory, String totalSizeCap) {} + + private static int initWithDefault(Config c, String key, int defaultValue) { + if (c.hasPath(key)) return c.getInt(key); + else return defaultValue; + } + + private static String stringWithDefault(Config c, String key, String defaultValue) { + if (c.hasPath(key)) return c.getString(key); + else return defaultValue; + } + + // Config keys + private static final String immediateFlushKey = "immediate-flush"; + private static final String appendKey = "append"; + private static final String patternKey = "pattern"; + + private static final String logLocationKey = "location"; + private static final String logRootKey = "log-root"; + private static final String logPrefixKey = "log-prefix"; + + private static final String rollingPolicyKey = "rolling-policy"; + private static final String maxFileSizeKey = "max-file-size"; + private static final String maxHistoryKey = "max-history"; + private static final String maxTotalSizeKey = "max-total-size"; + + public static final String appenderName = "file"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java new file mode 100644 index 000000000000..f4a892fc46db --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java @@ -0,0 +1,31 @@ +package org.enso.logger.config; + +import java.nio.file.Path; +import org.slf4j.event.Level; + +/** Base classes to be implemented by the underlying loggging implementation. */ +public abstract class LoggerSetup { + + /** Returns the parsed config used to create this instance * */ + public abstract LoggingServiceConfig getConfig(); + + public abstract boolean setupSocketAppender(Level logLevel, String hostname, int port); + + public abstract boolean setupFileAppender(Level logLevel, Path logRoot, String logPrefix); + + public abstract boolean setupConsoleAppender(Level logLevel); + + public abstract boolean setupSentryAppender(Level logLevel); + + public abstract boolean setupNoOpAppender(); + + public abstract Boolean setup() throws MissingConfigurationField; + + public abstract Boolean setup(Level logLevel) throws MissingConfigurationField; + + public abstract Boolean setup( + Level logLevel, + Path componentLogPath, + String componentLogPrefix, + LoggingServiceConfig config); +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java index e58938e53673..934692b1ed61 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggersLevels.java @@ -1,12 +1,14 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.slf4j.event.Level; +/** Encapsulates custom log levels that can be set via config file and environmental variables. */ public class LoggersLevels { private Map loggers; @@ -19,6 +21,10 @@ public Set> entrySet() { return loggers.entrySet(); } + public static LoggersLevels parse() { + return parse(ConfigFactory.empty()); + } + public static LoggersLevels parse(Config config) { // LinkedHashMap ensures that wildcard loggers are de-prioritized Map loggers = systemLoggers(); @@ -45,6 +51,10 @@ public static LoggersLevels parse(Config config) { return new LoggersLevels(loggers); } + public boolean isEmpty() { + return loggers.isEmpty(); + } + /** * Read any loggers' levels set via `-Dfoo.bar.Logger.level=` env variables. * diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java index 3a520c2dab75..8c4bd4c66250 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java @@ -4,7 +4,7 @@ public record LoggingServer(String hostname, int port, Appender appender, Boolean start) { - public static LoggingServer parse(Config config) { + public static LoggingServer parse(Config config) throws MissingConfigurationField { int port = config.getInt("port"); String hostname = config.getString("hostname"); Appender appender = Appender.parse(config.getConfig("appender")); diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java index ec84bf8be1b3..cc0661662814 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java @@ -13,7 +13,7 @@ public class LoggingServiceConfig { public static final String serverKey = "server"; public static final String loggersKey = "logger"; public static final String appendersKey = "appenders"; - public static final String defaultAppenderKey = "defaultAppender"; + public static final String defaultAppenderKey = "default-appender"; public static final String logLevelKey = "log-level"; private final LoggersLevels loggers; @@ -36,7 +36,7 @@ private LoggingServiceConfig( this.server = server; } - public static LoggingServiceConfig parseConfig() { + public static LoggingServiceConfig parseConfig() throws MissingConfigurationField { var empty = ConfigFactory.empty().atKey(configurationRoot); var root = ConfigFactory.load().withFallback(empty).getConfig(configurationRoot); LoggingServer server; @@ -58,7 +58,7 @@ public static LoggingServiceConfig parseConfig() { if (root.hasPath(loggersKey)) { loggers = LoggersLevels.parse(root.getConfig(loggersKey)); } else { - loggers = null; + loggers = LoggersLevels.parse(); } return new LoggingServiceConfig( loggers, @@ -68,6 +68,13 @@ public static LoggingServiceConfig parseConfig() { server); } + public static LoggingServiceConfig withSingleAppender(Appender appender) { + Map map = new HashMap<>(); + map.put(appender.getName(), appender); + return new LoggingServiceConfig( + LoggersLevels.parse(), Optional.empty(), map, appender.getName(), null); + } + public LoggersLevels getLoggers() { return loggers; } @@ -76,8 +83,20 @@ public Appender getAppender() { return appenders.get(defaultAppenderName); } - public Appender getAppender(String name) { - return appenders.get(name); + public SocketAppender getSocketAppender() { + return (SocketAppender) appenders.getOrDefault(SocketAppender.appenderName, null); + } + + public FileAppender getFileAppender() { + return (FileAppender) appenders.getOrDefault(FileAppender.appenderName, null); + } + + public ConsoleAppender getConsoleAppender() { + return (ConsoleAppender) appenders.getOrDefault(ConsoleAppender.appenderName, null); + } + + public SentryAppender getSentryAppender() { + return (SentryAppender) appenders.getOrDefault(SentryAppender.appenderName, null); } public boolean loggingServerNeedsBoot() { diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/MissingConfigurationField.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/MissingConfigurationField.java new file mode 100644 index 000000000000..95749dd6d93c --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/MissingConfigurationField.java @@ -0,0 +1,7 @@ +package org.enso.logger.config; + +public class MissingConfigurationField extends Exception { + public MissingConfigurationField(String name) { + super("Missing required configuration for field `" + name + "`"); + } +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java index 281324af5111..491290d6d6d5 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -3,27 +3,34 @@ import com.typesafe.config.Config; import org.slf4j.event.Level; +/** Config for log configuration that sends logs to sentry.io service. */ public class SentryAppender extends Appender { - private String name; + + public String getDsn() { + return dsn; + } + private String dsn; - private SentryAppender(String dsn, Config config) { - super(config); - this.name = "sentry"; + private SentryAppender(String dsn) { this.dsn = dsn; } - public static Appender parse(Config config) { - return new SentryAppender(config.getString("dsn"), config); + public static Appender parse(Config config) throws MissingConfigurationField { + if (config.hasPath(dsnKey)) return new SentryAppender(config.getString(dsnKey)); + else throw new MissingConfigurationField(dsnKey); } @Override - public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { - return appenderSetup.setupSentryAppender(logLevel, dsn); + public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + return appenderSetup.setupSentryAppender(logLevel); } @Override public String getName() { - return name; + return appenderName; } + + private static final String dsnKey = "dsn"; + public static final String appenderName = "sentry"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java index fc14be8892c7..7f202f6ff9ba 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -4,29 +4,39 @@ import java.net.URI; import org.slf4j.event.Level; +/** Config for log configuration that forwards logs to the network socket as-is. */ public class SocketAppender extends Appender { private String name; - private String hostname; + private String host; - public String getHostname() { - return hostname; + /** + * Returns the name of the host of the network socket to connect to. + * + * @return + */ + public String getHost() { + return host; } + /** Returns the port of the network socket to connect to. */ public int getPort() { return port; } private int port; - private int reconnectionDelay; - private boolean includeCallerData; - private SocketAppender(String hostname, int port, Config config) { - super(config); - this.name = "socket"; - this.hostname = hostname; + /** Returns the number of miliseconds after a failed connection should be re-established. */ + public int getReconnectionDelay() { + return reconnectionDelay; + } + + private final int reconnectionDelay; + + private SocketAppender(String host, int port, int reconnectionDelay) { + this.name = appenderName; + this.host = host; this.port = port; - this.reconnectionDelay = 10000; - this.includeCallerData = true; + this.reconnectionDelay = reconnectionDelay; } @Override @@ -34,22 +44,31 @@ public String getName() { return name; } - public static Appender parse(Config config) { - return new SocketAppender(config.getString("hostname"), config.getInt("port"), config); + public static Appender parse(Config config) throws MissingConfigurationField { + if (!config.hasPath(hostKey)) throw new MissingConfigurationField(hostKey); + if (!config.hasPath(portKey)) throw new MissingConfigurationField(portKey); + int reconnectionDelay = + config.hasPath(reconnectionDelayKey) ? config.getInt(reconnectionDelayKey) : 10000; + return new SocketAppender(config.getString(hostKey), config.getInt(portKey), reconnectionDelay); } @Override - public Boolean setup(Level logLevel, AppenderSetup appenderSetup) { - return appenderSetup.setupSocketAppender(logLevel, hostname, port); + public Boolean setup(Level logLevel, LoggerSetup loggerSetup) { + return loggerSetup.setupSocketAppender(logLevel, host, port); } @Override - public Boolean setupForURI(Level logLevel, String host, int port, AppenderSetup appenderSetup) { - return appenderSetup.setupSocketAppender(logLevel, host, port); + public Boolean setupForURI(Level logLevel, String host, int port, LoggerSetup loggerSetup) { + return loggerSetup.setupSocketAppender(logLevel, host, port); } @Override public boolean isSameTargetAs(URI uri) { - return uri.getHost().equals(hostname) && uri.getPort() == port; + return uri.getHost().equals(host) && uri.getPort() == port; } + + private static final String hostKey = "hostname"; + private static final String portKey = "port"; + private static final String reconnectionDelayKey = "reconnection-delay"; + public static final String appenderName = "socket"; } diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java index 15d1a78d01d4..0ae4623dcbdb 100644 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** java.util.logging.Handler that propagates all events to the equivalent SLF4J implementation. */ public class JulHandler extends Handler { private Formatter formattter; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java new file mode 100644 index 000000000000..c6ad6b7fdb58 --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -0,0 +1,289 @@ +package org.enso.logger; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.net.SocketAppender; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.helpers.NOPAppender; + +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.FileSize; +import io.sentry.SentryOptions; +import io.sentry.logback.SentryAppender; + +import java.io.File; +import java.nio.file.Path; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import org.enso.logger.config.*; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +public class LogbackSetup extends LoggerSetup { + + + private LogbackSetup(LoggingServiceConfig config, LoggerContext context) { + this.config = config; + this.context = context; + } + + private static LogbackSetup _setup = null; + private final static Object lock = new Object(); + + /** Get a singleton instance of LoggerSetup */ + public static LoggerSetup get() throws MissingConfigurationField { + if (_setup == null) { + synchronized (lock) { + if (_setup == null) { + _setup = new LogbackSetup(LoggingServiceConfig.parseConfig(), (LoggerContext) LoggerFactory.getILoggerFactory()); + } + } + } + return _setup; + } + + /** + * Create a logger setup for a provided context and a single appender configuration + * @param context context that will be initialized by this setup + * @param appender appender configuration to use during initialization + */ + public static LogbackSetup forContext(LoggerContext context, Appender appender) { + return new LogbackSetup(LoggingServiceConfig.withSingleAppender(appender), context); + } + + + public LoggingServiceConfig getConfig() { + return config; + } + + private final LoggingServiceConfig config; + private final LoggerContext context; + + @Override + public Boolean setup() throws MissingConfigurationField { + LoggingServiceConfig config = LoggingServiceConfig.parseConfig(); + return setup(config); + } + + private Boolean setup(LoggingServiceConfig config) { + Level defaultLogLevel = config.getLogLevel().map(name -> Level.valueOf(name.toUpperCase())).orElseGet(() -> Level.ERROR); + return setup(defaultLogLevel, config); + } + + @Override + public Boolean setup(Level logLevel) throws MissingConfigurationField { + return setup(logLevel, LoggingServiceConfig.parseConfig()); + } + + public Boolean setup(Level logLevel, LoggingServiceConfig config) { + Appender defaultAppender = config.getAppender(); + if (defaultAppender != null) { + return defaultAppender.setup(logLevel, this); + } else { + return setupConsoleAppender(logLevel); + } + } + + @Override + public Boolean setup(Level logLevel, Path componentLogPath, String componentLogPrefix, LoggingServiceConfig config) { + Appender defaultAppender = config.getAppender(); + if (defaultAppender != null) { + return defaultAppender.setupForPath(logLevel, componentLogPath, componentLogPrefix, this); + } else { + return setupConsoleAppender(logLevel); + } + } + + @Override + public boolean setupSocketAppender( + Level logLevel, + String hostname, + int port) { + LoggerAndContext env = contextInit(logLevel, config); + + org.enso.logger.config.SocketAppender appenderConfig = config.getSocketAppender(); + + SocketAppender socketAppender = new SocketAppender(); + socketAppender.setName("enso-socket"); + socketAppender.setIncludeCallerData(false); + socketAppender.setRemoteHost(hostname); + socketAppender.setPort(port); + if (appenderConfig != null) + socketAppender.setReconnectionDelay(Duration.buildByMilliseconds(appenderConfig.getReconnectionDelay())); + + env.finalizeAppender(socketAppender); + + return true; + } + + + @Override + public boolean setupFileAppender( + Level logLevel, + Path logRoot, + String logPrefix) { + try { + LoggerAndContext env = contextInit(logLevel, config); + + org.enso.logger.config.FileAppender appenderConfig = config.getFileAppender(); + if (appenderConfig == null) { + throw new MissingConfigurationField(org.enso.logger.config.FileAppender.appenderName); + } + final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setPattern(appenderConfig.getPattern()); + env.finalizeEncoder(encoder); + + FileAppender fileAppender; + + + if (appenderConfig != null && appenderConfig.getRollingPolicy() != null) { + RollingFileAppender rollingFileAppender = new RollingFileAppender<>(); + fileAppender = rollingFileAppender; + fileAppender.setContext(env.ctx); // Context needs to be set prior to rolling policy initialization + String filePattern; + if (logRoot == null || logPrefix == null) { + filePattern = "enso-%d{yyyy-MM-dd}"; + } else { + filePattern = logRoot.toAbsolutePath() + File.separator + logPrefix + "-" + "%d{yyyy-MM-dd}"; + } + + org.enso.logger.config.FileAppender.RollingPolicy rollingPolicy = appenderConfig.getRollingPolicy(); + SizeAndTimeBasedRollingPolicy logbackRollingPolicy = new SizeAndTimeBasedRollingPolicy(); + logbackRollingPolicy.setContext(env.ctx); + logbackRollingPolicy.setParent(fileAppender); + logbackRollingPolicy.setMaxFileSize(FileSize.valueOf(rollingPolicy.maxFileSize())); + logbackRollingPolicy.setMaxHistory(rollingPolicy.maxHistory()); + logbackRollingPolicy.setTotalSizeCap(FileSize.valueOf(rollingPolicy.totalSizeCap())); + logbackRollingPolicy.setFileNamePattern(filePattern + ".%i.log.gz"); + logbackRollingPolicy.start(); + + rollingFileAppender.setRollingPolicy(logbackRollingPolicy); + } else { + fileAppender = new FileAppender<>(); + fileAppender.setName("enso-file"); + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String currentDate = LocalDate.now().format(dtf); + String fullFilePath; + if (logRoot == null || logPrefix == null) { + fullFilePath = "enso-" + currentDate + ".log"; + } else { + fullFilePath = logRoot.toAbsolutePath() + File.separator + logPrefix + "-" + currentDate + ".log"; + } + fileAppender.setFile(fullFilePath); + } + + fileAppender.setAppend(true); + fileAppender.setImmediateFlush(true); + fileAppender.setEncoder(encoder); + + + env.finalizeAppender(fileAppender); + } catch (Throwable e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public boolean setupConsoleAppender(Level logLevel) { + LoggerAndContext env = contextInit(logLevel, config); + + org.enso.logger.config.ConsoleAppender appenderConfig = config.getConsoleAppender(); + final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + try { + encoder.setPattern(appenderConfig.getPattern()); + } catch (Throwable e) { + e.printStackTrace(); + encoder.setPattern(Appender.defaultPattern); + } + env.finalizeEncoder(encoder); + + ConsoleAppender consoleAppender = new ConsoleAppender<>(); + consoleAppender.setName("enso-console"); + consoleAppender.setEncoder(encoder); + + env.finalizeAppender(consoleAppender); + return true; + } + + @Override + public boolean setupSentryAppender(Level logLevel) { + try { + LoggerAndContext env = contextInit(logLevel, config); + + org.enso.logger.config.SentryAppender appenderConfig = config.getSentryAppender(); + if (appenderConfig == null) { + throw new MissingConfigurationField(org.enso.logger.config.SentryAppender.appenderName); + } + SentryAppender appender = new SentryAppender(); + SentryOptions opts = new SentryOptions(); + opts.setDsn(appenderConfig.getDsn()); + appender.setOptions(opts); + + env.finalizeAppender(appender); + } catch (Throwable e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public boolean setupNoOpAppender() { + LoggerAndContext env = contextInit(Level.ERROR, null); + + NOPAppender appender = new NOPAppender<>(); + appender.setName("enso-noop"); + + env.finalizeAppender(appender); + return true; + } + + public static void teardown() { + // TODO: disable whatever appender is now in place and replace it with console + var context = (LoggerContext) LoggerFactory.getILoggerFactory(); + context.stop(); + } + + private LoggerAndContext contextInit(Level level, LoggingServiceConfig config) { + context.reset(); + context.setName("enso-custom"); + Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); + + Filter filter; + LoggersLevels loggers = config != null ? config.getLoggers() : null; + if (loggers != null && !loggers.isEmpty()) { + filter = ApplicationFilter.fromLoggers(loggers); + } else { + filter = null; + } + return new LoggerAndContext(level, context, rootLogger, filter); + } + + private record LoggerAndContext(Level level, LoggerContext ctx, Logger logger, Filter filter) { + + void finalizeEncoder(ch.qos.logback.core.encoder.Encoder encoder) { + encoder.setContext(ctx); + encoder.start(); + } + void finalizeAppender(ch.qos.logback.core.Appender appender) { + logger.setLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(level)); + if (filter != null) { + appender.addFilter(filter); + filter.setContext(ctx); + filter.start(); + } + appender.setContext(ctx); + appender.start(); + logger.addAppender(appender); + } + } +} diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java deleted file mode 100644 index 21b7590f54ad..000000000000 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LoggerSetup.java +++ /dev/null @@ -1,231 +0,0 @@ -package org.enso.logger; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.net.SocketAppender; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.encoder.PatternLayoutEncoder; -import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.ConsoleAppender; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.helpers.NOPAppender; - -import io.sentry.SentryOptions; -import io.sentry.logback.SentryAppender; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - -import org.enso.logger.config.AppenderSetup; -import org.enso.logger.config.LoggingServiceConfig; -import org.enso.logger.config.Appender; -import org.enso.logger.config.LoggersLevels; -import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; - -public class LoggerSetup extends AppenderSetup { - - private static LoggerSetup _setup = null; - - private final static Object lock = new Object(); - - public LoggingServiceConfig getConfig() { - return config; - } - - private final LoggingServiceConfig config; - - private LoggerSetup(LoggingServiceConfig config) { - this.config = config; - } - - public static LoggerSetup get() { - if (_setup == null) { - synchronized (lock) { - if (_setup == null) { - _setup = new LoggerSetup(LoggingServiceConfig.parseConfig()); - } - } - } - return _setup; - } - - private static final String defaultPattern = "[%level] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n"; - - private static final String fileAppenderKey = "file"; - private static final String socketAppenderKey = "socket"; - private static final String consoleAppenderKey = "console"; - - public Boolean setup() { - LoggingServiceConfig config = LoggingServiceConfig.parseConfig(); - return setup(config); - } - - private Boolean setup(LoggingServiceConfig config) { - Level defaultLogLevel = config.getLogLevel().map(name -> Level.valueOf(name.toUpperCase())).orElseGet(() -> Level.ERROR); - return setup(defaultLogLevel, config); - } - - public Boolean setup(Level logLevel) { - return setup(logLevel, LoggingServiceConfig.parseConfig()); - } - - public Boolean setup(Level logLevel, LoggingServiceConfig config) { - Appender defaultAppender = config.getAppender(); - if (defaultAppender != null) { - return defaultAppender.setup(logLevel, this); - } else { - return setupConsoleAppender(logLevel); - } - } - - public Boolean setup(Level logLevel, Path componentLogPath, String componentLogPrefix, LoggingServiceConfig config) { - Appender defaultAppender = config.getAppender(); - if (defaultAppender != null) { - return defaultAppender.setupForPath(logLevel, componentLogPath, componentLogPrefix, this); - } else { - return setupConsoleAppender(logLevel); - } - } - - @Override - public boolean setupSocketAppender( - Level logLevel, - String hostname, - int port) { - LoggerAndContext env = contextInit(logLevel, config); - - SocketAppender socketAppender = new SocketAppender(); - socketAppender.setName("enso-socket"); - socketAppender.setIncludeCallerData(true); - socketAppender.setRemoteHost(hostname); - socketAppender.setPort(port); - - env.finalizeAppender(socketAppender); - - return true; - } - - - @Override - public boolean setupFileAppender( - Level logLevel, - Path logRoot, - String logPrefix) { - LoggerAndContext env = contextInit(logLevel, config); - - String configPattern = ((org.enso.logger.config.FileAppender)config.getAppender(fileAppenderKey)).getPattern(); - final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setPattern(configPattern == null ? defaultPattern : configPattern); - env.finalizeEncoder(encoder); - - FileAppender fileAppender = new FileAppender<>(); - fileAppender.setName("enso-file"); - fileAppender.setAppend(true); - fileAppender.setImmediateFlush(true); - DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - String currentDate = LocalDate.now().format(dtf); - String fullFilePath; - if (logRoot == null || logPrefix == null) { - fullFilePath = "enso-" + currentDate + ".log"; - } else { - fullFilePath = logRoot.toAbsolutePath() + File.separator + logPrefix + "-" + currentDate + ".log"; - } - fileAppender.setEncoder(encoder); - fileAppender.setFile(fullFilePath); - - env.finalizeAppender(fileAppender); - return true; - } - - @Override - public boolean setupConsoleAppender(Level logLevel) { - LoggerAndContext env = contextInit(logLevel, config); - - String consolePattern; - if (config != null) { - consolePattern = ((org.enso.logger.config.ConsoleAppender)config.getAppender(consoleAppenderKey)).getPattern(); - } else { - consolePattern = null; - } - final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setPattern(consolePattern == null ? defaultPattern : consolePattern); - env.finalizeEncoder(encoder); - - ConsoleAppender consoleAppender = new ConsoleAppender<>(); - consoleAppender.setName("enso-console"); - consoleAppender.setEncoder(encoder); - - env.finalizeAppender(consoleAppender); - return true; - } - - @Override - public boolean setupSentryAppender(Level logLevel, String dsn) { - LoggerAndContext env = contextInit(logLevel, config); - - SentryAppender appender = new SentryAppender(); - SentryOptions opts = new SentryOptions(); - opts.setDsn(dsn); - appender.setOptions(opts); - - env.finalizeAppender(appender); - return true; - } - - @Override - public boolean setupNoOpAppender() { - LoggerAndContext env = contextInit(Level.ERROR, null); - - NOPAppender appender = new NOPAppender<>(); - appender.setName("enso-noop"); - - env.finalizeAppender(appender); - return true; - } - - public static void teardown() { - // TODO: disable whatever appender is now in place and replace it with console - var context = (LoggerContext) LoggerFactory.getILoggerFactory(); - context.stop(); - } - - private LoggerAndContext contextInit(Level level, LoggingServiceConfig config) { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - context.reset(); - context.setName("enso-custom"); - Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); - - Filter filter; - LoggersLevels loggers = config != null ? config.getLoggers() : null; - if (loggers != null) { - filter = ApplicationFilter.fromLoggers(loggers); - } else { - filter = null; - } - return new LoggerAndContext(level, context, rootLogger, filter); - } - - private record LoggerAndContext(Level level, LoggerContext ctx, Logger logger, Filter filter) { - - void finalizeEncoder(ch.qos.logback.core.encoder.Encoder encoder) { - encoder.setContext(ctx); - encoder.start(); - } - void finalizeAppender(ch.qos.logback.core.Appender appender) { - logger.setLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(level)); - if (filter != null) { - appender.addFilter(filter); - filter.setContext(ctx); - filter.start(); - } - appender.setContext(ctx); - appender.start(); - logger.addAppender(appender); - } - } -} diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java index fdecdaaf43ab..4940fd9e7a9c 100644 --- a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java +++ b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java @@ -1,12 +1,13 @@ package org.enso.logging; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.net.SimpleSocketServer; import ch.qos.logback.core.joran.spi.JoranException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; +import org.enso.logger.LogbackSetup; +import org.enso.logger.config.Appender; import org.slf4j.event.Level; class LoggingServer extends LoggingService { @@ -19,16 +20,11 @@ public LoggingServer(int port) { this.logServer = null; } - public URI start(Level level, Path path, String prefix, String appenderName) + public URI start(Level level, Path path, String prefix, Appender appender) throws URISyntaxException, JoranException { var lc = new LoggerContext(); - var configurator = new JoranConfigurator(); - System.setProperty("logging-server.logRoot", path.toAbsolutePath().toString()); - System.setProperty("logging-server.logPrefix", prefix); - System.setProperty("logging-server.logLevel", level.toString().toLowerCase()); - System.setProperty("logging-server.appender", appenderName); - configurator.setContext(lc); - configurator.doConfigure(this.getClass().getResourceAsStream("/logging-server.logback.xml")); + var setup = LogbackSetup.forContext(lc, appender); + setup.setup(level, path, prefix, setup.getConfig()); logServer = new SimpleSocketServer(lc, port); logServer.start(); return new URI(null, null, "localhost", port, null, null, null); diff --git a/lib/scala/logging-server/src/main/resources/logging-server.logback.xml b/lib/scala/logging-server/src/main/resources/logging-server.logback.xml deleted file mode 100644 index 42bcf414e2e6..000000000000 --- a/lib/scala/logging-server/src/main/resources/logging-server.logback.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.log - ${logging-server.logRoot}/${logging-server.logPrefix}-%d{yyyy-MM-dd}.%i.log.gz - 50MB - 30 - 2GB - - true - true - - [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n - - - - - [%level] [%d{yyyy-MM-dd'T'HH:mm:ssXXX, UTC}] [%logger] %msg%n - - - - - - - - - - diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala index c5d72af9a1a2..34cd63162d61 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -1,5 +1,7 @@ package org.enso.logging +import org.enso.logger.config.Appender + import java.net.URI import org.slf4j.event.Level @@ -19,7 +21,7 @@ object LoggingServiceManager { port: Int, logPath: Path, logFileSuffix: String, - appenderName: String + appender: Appender )(implicit ec: ExecutionContext): Future[URI] = { if (loggingService != null) { throw new LoggingServiceAlreadySetup() @@ -28,7 +30,7 @@ object LoggingServiceManager { val forwarder = new LoggingServer(port) loggingService = forwarder Future { - forwarder.start(logLevel, logPath, logFileSuffix, appenderName) + forwarder.start(logLevel, logPath, logFileSuffix, appender) } } } diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala index 77cb03f90b52..7979ee91ac33 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala @@ -1,5 +1,7 @@ package org.enso.logging +import org.enso.logger.LogbackSetup +import org.enso.logger.config.LoggerSetup import org.slf4j.event.Level import java.net.URI @@ -8,7 +10,6 @@ import scala.concurrent.Future import scala.concurrent.ExecutionContext import scala.concurrent.Promise import scala.util.{Failure, Success} -import org.enso.logger.LoggerSetup import org.enso.logger.masking.Masking import scala.concurrent.Await @@ -30,7 +31,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { * Some logs may be added while inferring the parameters of logging infrastructure, leading to catch-22 situations. */ def initLogger(): Unit = { - LoggerSetup.get().setupNoOpAppender() + LogbackSetup.get().setupNoOpAppender() } /** Starts a logging server that collects logs from different components and immediate sets up logs from this component @@ -42,7 +43,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { logLevel: Option[Level], logMasking: Boolean ): Unit = { - val loggerSetup = LoggerSetup.get() + val loggerSetup = LogbackSetup.get() val config = loggerSetup.getConfig if (config.loggingServerNeedsBoot()) { val actualPort = config.getServer().port(); @@ -53,7 +54,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { actualPort, logPath, logFileSuffix, - config.getServer().appender().getName() + config.getServer().appender() ) .onComplete { case Failure(_) => @@ -89,7 +90,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { logLevel, connectToExternalLogger, logMasking, - LoggerSetup.get() + LogbackSetup.get() ); } diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index c3bd8c005f03..546931c2c71e 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -37,8 +37,8 @@ logging-service { name = "console" } ] - defaultAppender = ${?DEFAULT_LOGGER} - defaultAppender = socket + default-appender = ${?DEFAULT_LOGGER} + default-appender = socket server { start = ${?LOGSERVER_START} start = true @@ -49,6 +49,11 @@ logging-service { appender { name = ${?LOGSERVER_APPENDER} name = "file" # file/console/socket/sentry + rolling-policy { + max-file-size = "100MB" + max-history = 30 + max-total-size = "2GB" + } } } } diff --git a/lib/scala/project-manager/src/test/resources/application.conf b/lib/scala/project-manager/src/test/resources/application.conf index 51e94e0864b6..29d57dbfec33 100644 --- a/lib/scala/project-manager/src/test/resources/application.conf +++ b/lib/scala/project-manager/src/test/resources/application.conf @@ -15,7 +15,7 @@ logging-service { pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] - defaultAppender = console + default-appender = console log-level = "warn" } diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala index 793ed9cd1384..663b2a5d44f3 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala @@ -16,7 +16,7 @@ import org.enso.editions.Editions import org.enso.cli.OS import org.enso.jsonrpc.test.JsonRpcServerTestKit import org.enso.jsonrpc.{ClientControllerFactory, ProtocolFactory} -import org.enso.logger.LoggerSetup +import org.enso.logger.LogbackSetup import org.enso.pkg.{Config, PackageManager} import org.enso.projectmanager.boot.Globals.{ConfigFilename, ConfigNamespace} import org.enso.projectmanager.boot.configuration._ @@ -237,9 +237,9 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { super.beforeAll() if (debugLogs) { - LoggerSetup.setup(Level.TRACE) + LogbackSetup.get().setup(Level.TRACE) } else { - LoggerSetup.setup() + LogbackSetup.get().setup() } setupEditions() diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala index a224b4d1cd9b..5003105fc3e9 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala @@ -3,11 +3,14 @@ package org.enso.projectmanager.infrastructure.languageserver import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{ImplicitSender, TestActor, TestKit, TestProbe} import com.miguno.akka.testing.VirtualTime -import org.enso.logger.LoggerSetup +import org.enso.logger.LogbackSetup import org.enso.projectmanager.boot.configuration.SupervisionConfig import org.enso.projectmanager.infrastructure.http.AkkaBasedWebSocketConnectionFactory import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootLoader.ServerBooted -import org.enso.projectmanager.infrastructure.languageserver.ProgrammableWebSocketServer.{Reject, ReplyWith} +import org.enso.projectmanager.infrastructure.languageserver.ProgrammableWebSocketServer.{ + Reject, + ReplyWith +} import org.enso.projectmanager.infrastructure.languageserver.StepParent.ChildTerminated import org.enso.projectmanager.infrastructure.net.Tcp import org.enso.testkit.FlakySpec @@ -104,7 +107,7 @@ class LanguageServerSupervisorSpec trait TestCtx { - LoggerSetup.setup() + LogbackSetup.get().setup() val VerificationTimeout = 120000 From d0a784ecabdcd7179c241f459f47105cb049b735 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 22 Aug 2023 22:56:48 +0200 Subject: [PATCH 14/31] logging-service - bye! --- build.sbt | 30 -- .../internal/NativeAnsiTerm.java | 18 - .../internal/NativeAnsiTerm.java | 109 ------ .../org/enso/logger/akka/package-info.java | 1 - .../org/slf4j/impl/StaticLoggerBinder.java | 32 -- .../java/org/slf4j/impl/StaticMDCBinder.java | 33 -- .../org/slf4j/impl/StaticMarkerBinder.java | 30 -- .../logger/akka/ActorLoggingReceive.scala | 56 ---- .../logger/akka/ActorMessageLogging.scala | 34 -- .../main/scala/org/enso/logger/package.scala | 18 - .../org/enso/loggingservice/ColorMode.scala | 20 -- .../JavaLoggingLogHandler.scala | 77 ----- .../org/enso/loggingservice/LogLevel.scala | 147 -------- .../org/enso/loggingservice/Logger.scala | 315 ------------------ .../enso/loggingservice/LoggerFactory.scala | 24 -- .../org/enso/loggingservice/LoggerMode.scala | 44 --- ...ngServiceAlreadyInitializedException.scala | 7 - .../LoggingServiceManager.scala | 195 ----------- .../LoggingServiceSetupHelper.scala | 261 --------------- .../enso/loggingservice/ServerBinding.scala | 13 - .../org/enso/loggingservice/TestLogger.scala | 54 --- .../internal/ANSIColorsMessageRenderer.scala | 28 -- .../internal/AnsiTerminal.scala | 54 --- .../internal/BaseLogMessage.scala | 15 - .../BlockingConsumerMessageQueue.scala | 88 ----- .../internal/DefaultLogMessageRenderer.scala | 75 ----- .../internal/InternalLogMessage.scala | 57 ---- .../internal/InternalLogger.scala | 17 - .../internal/LogMessageRenderer.scala | 13 - .../internal/LoggerConnection.scala | 40 --- .../internal/LoggingSettings.scala | 81 ----- .../internal/TestMessageQueue.scala | 35 -- .../protocol/SerializedException.scala | 191 ----------- .../internal/protocol/WSLogMessage.scala | 80 ----- .../internal/service/Client.scala | 169 ---------- .../internal/service/Local.scala | 49 --- .../internal/service/Server.scala | 174 ---------- .../internal/service/Service.scala | 10 - .../service/ServiceWithActorSystem.scala | 121 ------- .../service/ThreadProcessingService.scala | 98 ------ .../printers/FileOutputPrinter.scala | 76 ----- .../printers/FileXmlPrinter.scala | 61 ---- .../loggingservice/printers/Printer.scala | 20 -- .../printers/StderrPrinter.scala | 32 -- .../printers/StderrPrinterWithColors.scala | 66 ---- .../loggingservice/printers/TestPrinter.scala | 38 --- .../JavaLoggingLogHandlerTest.java | 52 --- .../DefaultLogMessageRendererSpec.scala | 59 ---- .../internal/SerializedExceptionSpec.scala | 48 --- .../internal/WSLogMessageSpec.scala | 44 --- .../service/ClientServerServiceSpec.scala | 81 ----- .../internal/service/LocalServiceSpec.scala | 11 - .../internal/service/ServiceTest.scala | 75 ----- 53 files changed, 3576 deletions(-) delete mode 100755 lib/scala/logging-service/src/main/java-unix/org/enso/loggingservice/internal/NativeAnsiTerm.java delete mode 100755 lib/scala/logging-service/src/main/java-windows/org/enso/loggingservice/internal/NativeAnsiTerm.java delete mode 100644 lib/scala/logging-service/src/main/java/org/enso/logger/akka/package-info.java delete mode 100644 lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticLoggerBinder.java delete mode 100644 lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMDCBinder.java delete mode 100644 lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMarkerBinder.java delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/logger/package.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ColorMode.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/JavaLoggingLogHandler.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LogLevel.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/Logger.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerFactory.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerMode.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceAlreadyInitializedException.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceManager.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceSetupHelper.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ServerBinding.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/TestLogger.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/ANSIColorsMessageRenderer.scala delete mode 100755 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/AnsiTerminal.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BaseLogMessage.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BlockingConsumerMessageQueue.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/DefaultLogMessageRenderer.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogMessage.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogger.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LogMessageRenderer.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggerConnection.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggingSettings.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/TestMessageQueue.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/SerializedException.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/WSLogMessage.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Client.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Local.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Server.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Service.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ServiceWithActorSystem.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ThreadProcessingService.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileOutputPrinter.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileXmlPrinter.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/Printer.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinter.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinterWithColors.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/TestPrinter.scala delete mode 100644 lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/DefaultLogMessageRendererSpec.scala delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/SerializedExceptionSpec.scala delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/WSLogMessageSpec.scala delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ClientServerServiceSpec.scala delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/LocalServiceSpec.scala delete mode 100644 lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ServiceTest.scala diff --git a/build.sbt b/build.sbt index bb97c92b2390..06f45d92b28a 100644 --- a/build.sbt +++ b/build.sbt @@ -272,7 +272,6 @@ lazy val enso = (project in file(".")) `logging-server`, `logging-utils-akka`, filewatcher, - `logging-service`, `logging-truffle-connector`, `locking-test-helper`, `akka-native`, @@ -759,35 +758,6 @@ lazy val `logging-utils-akka` = project ) ) -lazy val `logging-service` = project - .in(file("lib/scala/logging-service")) - .configs(Test) - .settings( - frgaalJavaCompilerSetting, - version := "0.1", - libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, - akkaStream, - akkaHttp, - "io.circe" %% "circe-core" % circeVersion, - "io.circe" %% "circe-parser" % circeVersion, - "junit" % "junit" % junitVersion % Test, - "com.github.sbt" % "junit-interface" % junitIfVersion % Test, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, - "org.graalvm.nativeimage" % "svm" % graalMavenPackagesVersion % "provided" - ) - ) - .settings( - if (Platform.isWindows) - (Compile / unmanagedSourceDirectories) += (Compile / sourceDirectory).value / "java-windows" - else - (Compile / unmanagedSourceDirectories) += (Compile / sourceDirectory).value / "java-unix" - ) - .dependsOn(`akka-native`) - .dependsOn(`logging-utils`) - lazy val filewatcher = project .in(file("lib/scala/filewatcher")) .configs(Test) diff --git a/lib/scala/logging-service/src/main/java-unix/org/enso/loggingservice/internal/NativeAnsiTerm.java b/lib/scala/logging-service/src/main/java-unix/org/enso/loggingservice/internal/NativeAnsiTerm.java deleted file mode 100755 index 1faedf91152a..000000000000 --- a/lib/scala/logging-service/src/main/java-unix/org/enso/loggingservice/internal/NativeAnsiTerm.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.enso.loggingservice.internal; - -/** - * Provides a stub for enabling VT console mode. - * - *

We assume that VT is supported by default on UNIX platforms, so this function is never - * actually used. It is defined just to provide binary compatibility with the Windows counterpart, - * so that the code that uses it only on Windows, compiles on all platforms. - */ -public class NativeAnsiTerm { - - /** - * Enables VT emulation within the connected console. - * - *

The UNIX variant does nothing, as we assume that VT is supported out of the box. - */ - public static void enableVT() {} -} diff --git a/lib/scala/logging-service/src/main/java-windows/org/enso/loggingservice/internal/NativeAnsiTerm.java b/lib/scala/logging-service/src/main/java-windows/org/enso/loggingservice/internal/NativeAnsiTerm.java deleted file mode 100755 index 91f846f29ba6..000000000000 --- a/lib/scala/logging-service/src/main/java-windows/org/enso/loggingservice/internal/NativeAnsiTerm.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.enso.loggingservice.internal; - -import org.graalvm.nativeimage.UnmanagedMemory; -import org.graalvm.nativeimage.c.function.CFunction; -import org.graalvm.nativeimage.c.type.CIntPointer; -import org.graalvm.word.PointerBase; - -/** Provides access to the native WinApi calls that enable VT emulation in a connected console. */ -public class NativeAnsiTerm { - - /** - * Returns a handle to a console connected to one of the standard streams. - * - *

Refer to: https://docs.microsoft.com/en-us/windows/console/getstdhandle - * - * @param nStdHandle constant representing one of the standard streams - * @return pointer to the console handle or null if it could not be accessed - */ - @CFunction - private static native PointerBase GetStdHandle(int nStdHandle); - - /** - * Returns current console mode. - * - * @param hConsoleHandle console handle from [[GetStdHandle]] - * @param lpMode pointer to an integer that will be set to the current mode on success - * @return non-zero integer on success - * @see SetConsoleMode - */ - @CFunction - private static native int SetConsoleMode(PointerBase hConsoleHandle, int dwMode); - - /** - * Returns error code of last error. - * - *

Can be called if a function returns a zero exit code to get the error code. - * - * @see GetLastError - */ - @CFunction - private static native int GetLastError(); - - /** - * Constant that can be used in [[GetStdHandle]] that refers to the standard error stream. - * - * @see GetStdHandle - */ - private static final int STD_ERROR_HANDLE = -12; - - /** - * Constant that can be used as part of a console mode which indicates that the output stream - * should handle VT escape codes. - * - * @see SetConsoleMode - * @see Console - * Virtual Terminal Sequences - */ - private static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; - - /** - * Enables VT emulation within the connected console. - * - *

May throw an exception if it is not possible to do so. Can only be called from native-image - * targets. - */ - public static void enableVT() { - CIntPointer modePtr = UnmanagedMemory.malloc(4); - try { - var handle = GetStdHandle(STD_ERROR_HANDLE); - if (handle.isNull()) { - throw new RuntimeException( - "Failed to get console handle. " - + "Perhaps the console is not connected. " - + "Error code: " - + GetLastError()); - } - if (GetConsoleMode(handle, modePtr) == 0) { - throw new RuntimeException( - "Failed to get console mode. " + "Error code: " + GetLastError()); - } - var alteredMode = modePtr.read() | ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (SetConsoleMode(handle, alteredMode) == 0) { - throw new RuntimeException( - "Failed to set console mode. " - + "Perhaps the console does not support VT codes. " - + "Error code: " - + GetLastError()); - } - } finally { - UnmanagedMemory.free(modePtr); - } - } -} diff --git a/lib/scala/logging-service/src/main/java/org/enso/logger/akka/package-info.java b/lib/scala/logging-service/src/main/java/org/enso/logger/akka/package-info.java deleted file mode 100644 index 6d03f00ecdf8..000000000000 --- a/lib/scala/logging-service/src/main/java/org/enso/logger/akka/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package org.enso.logger.akka; diff --git a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticLoggerBinder.java deleted file mode 100644 index 7018a88b7894..000000000000 --- a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.slf4j.impl; - -import org.enso.loggingservice.LoggerFactory; -import org.slf4j.ILoggerFactory; - -/** - * Binds the logging service as an SLF4J backend. - * - *

The public interface of this class must conform to what is expected by an SLF4J backend. See - * slf4j-simple for reference. - */ -public class StaticLoggerBinder { - /** Should be in sync with `slf4jVersion` in `build.sbt`. */ - public static String REQUESTED_API_VERSION = "1.7.36"; - - private static final StaticLoggerBinder singleton = new StaticLoggerBinder(); - - public static StaticLoggerBinder getSingleton() { - return singleton; - } - - private final LoggerFactory factory = new LoggerFactory(); - private final String factoryClassStr = LoggerFactory.class.getName(); - - public ILoggerFactory getLoggerFactory() { - return factory; - } - - public String getLoggerFactoryClassStr() { - return factoryClassStr; - } -} diff --git a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMDCBinder.java deleted file mode 100644 index 55f3e7c8b7e1..000000000000 --- a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMDCBinder.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.slf4j.impl; - -import org.slf4j.helpers.NOPMDCAdapter; -import org.slf4j.spi.MDCAdapter; - -/** - * Provides a no-op MDC adapter for the SLF4J backend. - * - *

MDC handling is an optional SLF4J feature and currently the logging service does not support - * it, so it provides a no-op adapter. - * - *

The public interface of this class must conform to what is expected by an SLF4J backend. See - * slf4j-simple for reference. - */ -public class StaticMDCBinder { - - private static final StaticMDCBinder singleton = new StaticMDCBinder(); - - public static StaticMDCBinder getSingleton() { - return singleton; - } - - private final MDCAdapter adapter = new NOPMDCAdapter(); - private final String adapterClassStr = NOPMDCAdapter.class.getName(); - - public MDCAdapter getMDCA() { - return adapter; - } - - public String getMDCAdapterClassStr() { - return adapterClassStr; - } -} diff --git a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMarkerBinder.java b/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMarkerBinder.java deleted file mode 100644 index b55cac48e634..000000000000 --- a/lib/scala/logging-service/src/main/java/org/slf4j/impl/StaticMarkerBinder.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.slf4j.impl; - -import org.slf4j.IMarkerFactory; -import org.slf4j.helpers.BasicMarkerFactory; - -/** - * Provides a simple marker factory for the SLF4J backend. - * - *

The public interface of this class must conform to what is expected by an SLF4J backend. See - * slf4j-simple for reference. - */ -public class StaticMarkerBinder { - - private static final StaticMarkerBinder singleton = new StaticMarkerBinder(); - - public static StaticMarkerBinder getSingleton() { - return singleton; - } - - private final IMarkerFactory markerFactory = new BasicMarkerFactory(); - private final String markerFactoryClassStr = BasicMarkerFactory.class.getName(); - - public IMarkerFactory getMarkerFactory() { - return markerFactory; - } - - public String getMarkerFactoryClassStr() { - return markerFactoryClassStr; - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala b/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala deleted file mode 100644 index 3382e6796398..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorLoggingReceive.scala +++ /dev/null @@ -1,56 +0,0 @@ -package org.enso.logger.akka - -import akka.actor.Actor.Receive -import akka.actor.ActorContext -import org.slf4j.Logger - -/** The decorator adding invocation logging to a Receive function of an actor. - * - * @param label the label `" in state " + label` appended to each message - * @param logger the logger that will be used for logging actor messages - * @param r the receive function of an actor - * @param context the actor context - */ -class ActorLoggingReceive( - label: Option[String], - logger: Logger, - r: Receive -)(implicit context: ActorContext) - extends Receive { - - /** @inheritdoc */ - override def isDefinedAt(o: Any): Boolean = { - val handled = r.isDefinedAt(o) - val labelText = label match { - case Some(l) => " in state " + l - case _ => "" - } - val template = - s"received ${if (handled) "handled" else "unhandled"} {} from {}$labelText" - logger.trace(template, o, context.sender()) - handled - } - - /** @inheritdoc */ - override def apply(o: Any): Unit = r(o) -} - -object ActorLoggingReceive { - - /** Create the [[ActorLoggingReceive]] decorator adding invocation logging - * to a Receive function. - * - * @param label the label `" in state " + label` appended to each message - * @param logger the logger - * @param r the original receive function - */ - def apply(label: Option[String], logger: Logger, r: Receive)(implicit - context: ActorContext - ): Receive = r match { - case _: ActorLoggingReceive => - r - case _ => - new ActorLoggingReceive(label, logger, r) - - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala b/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala deleted file mode 100644 index b33065b132fd..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/logger/akka/ActorMessageLogging.scala +++ /dev/null @@ -1,34 +0,0 @@ -package org.enso.logger.akka - -import akka.actor.{Actor, ActorContext} -import org.slf4j.LoggerFactory - -/** A trait providing functions for logging received actor messages. */ -trait ActorMessageLogging { outer: Actor => - - object LoggingReceive { - - private val logger = LoggerFactory.getLogger(outer.getClass) - - /** Wrap a Receive partial function in a logging enclosure, which logs a - * message each time before a message is matched. This includes messages - * which are not handled. - * - * {{{ - * def receive = LoggingReceive { - * case x => ... - * } - * }}} - */ - def apply(r: Receive)(implicit context: ActorContext): Receive = - ActorLoggingReceive(None, logger, r) - - /** Create a decorated logger which appends `" in state " + label` - * to each message it logs. - */ - def withLabel(label: String)(r: Receive)(implicit - context: ActorContext - ): Receive = - ActorLoggingReceive(Some(label), logger, r) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/logger/package.scala b/lib/scala/logging-service/src/main/scala/org/enso/logger/package.scala deleted file mode 100644 index a202e35b0324..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/logger/package.scala +++ /dev/null @@ -1,18 +0,0 @@ -package org.enso - -import com.typesafe.scalalogging.Logger - -package object logger { - - /** Provides syntax for entering a sub-logger. - */ - implicit class LoggerSyntax(logger: Logger) { - - /** Returns another [[Logger]] with name extended with a sub context. - */ - def enter(subContextName: String): Logger = { - val name = logger.underlying.getName + "." + subContextName - Logger(name) - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ColorMode.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ColorMode.scala deleted file mode 100644 index 1a7e1fbb9565..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ColorMode.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.enso.loggingservice - -/** Describes possible modes of color display in console output. */ -sealed trait ColorMode -object ColorMode { - - /** Never use color escape sequences in the output. */ - case object Never extends ColorMode - - /** Enable color output if it seems to be supported. */ - case object Auto extends ColorMode - - /** Always use escape sequences in the output, even if the program thinks they - * are unsupported. - * - * May be useful if output is piped to other programs that know how to handle - * the escape sequences. - */ - case object Always extends ColorMode -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/JavaLoggingLogHandler.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/JavaLoggingLogHandler.scala deleted file mode 100644 index 85a069f9e872..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/JavaLoggingLogHandler.scala +++ /dev/null @@ -1,77 +0,0 @@ -package org.enso.loggingservice - -import org.enso.loggingservice.internal.{InternalLogMessage, LoggerConnection} - -import java.util.logging.{Handler, Level, LogRecord, SimpleFormatter} - -/** A [[Handler]] implementation that allows to use the logging service as a - * backend for [[java.util.logging]]. - */ -class JavaLoggingLogHandler( - levelMapping: Level => LogLevel, - connection: LoggerConnection -) extends Handler { - - /** @inheritdoc - */ - override def publish(record: LogRecord): Unit = { - val level = levelMapping(record.getLevel) - if (connection.isEnabled(record.getLoggerName, level)) { - val message = InternalLogMessage( - level = level, - timestamp = record.getInstant, - group = record.getLoggerName, - message = JavaLoggingLogHandler.formatter.formatMessage(record), - exception = Option(record.getThrown) - ) - connection.send(message) - } - } - - /** @inheritdoc */ - override def flush(): Unit = {} - - /** @inheritdoc */ - override def close(): Unit = {} -} - -object JavaLoggingLogHandler { - private val formatter = new SimpleFormatter() - - /** Creates a [[Handler]] with the provided mapping from Java's log levels to - * our log levels. - */ - def create(mapping: Level => LogLevel): JavaLoggingLogHandler = - new JavaLoggingLogHandler(mapping, LoggingServiceManager.Connection) - - /** Determines what is the smallest Java level that is still debug and not - * trace. - */ - private val defaultLevelDebugCutOff = - Seq(Level.FINE.intValue, Level.CONFIG.intValue).min - - /** Default mapping of Java log levels to our log levels based - */ - def defaultLevelMapping(javaLevel: Level): LogLevel = { - val level = javaLevel.intValue - if (level == Level.OFF.intValue) LogLevel.Off - else if (level >= Level.SEVERE.intValue) LogLevel.Error - else if (level >= Level.WARNING.intValue) LogLevel.Warning - else if (level >= Level.INFO.intValue) LogLevel.Info - else if (level >= defaultLevelDebugCutOff) LogLevel.Debug - else LogLevel.Trace - } - - /** Approximate-inverse of [[defaultLevelMapping]] that returns a Java log - * level corresponding to the given log level. - */ - def getJavaLogLevelFor(logLevel: LogLevel): Level = - logLevel match { - case LogLevel.Off => Level.OFF - case LogLevel.Error => Level.SEVERE - case LogLevel.Warning => Level.WARNING - case LogLevel.Info => Level.INFO - case LogLevel.Debug => Level.FINE - case LogLevel.Trace => Level.ALL - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LogLevel.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LogLevel.scala deleted file mode 100644 index 6949d1767565..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LogLevel.scala +++ /dev/null @@ -1,147 +0,0 @@ -package org.enso.loggingservice - -import io.circe.syntax._ -import io.circe.{Decoder, DecodingFailure, Encoder} - -/** Defines a log level for log messages. */ -sealed abstract class LogLevel(final val name: String, final val level: Int) { - - /** Determines if a component running on `this` log level should log the - * `other`. - * - * Log levels smaller or equal to component's log level are logged. - */ - def shouldLog(other: LogLevel): Boolean = - other.level <= level - - /** @inheritdoc */ - override def toString: String = name -} - -object LogLevel { - - /** This log level should not be used by messages, instead it can be set as - * component's log level to completely disable logging for it. - */ - case object Off extends LogLevel("off", -1) - - /** Log level corresponding to severe errors, should be understandable to the - * end-user. - */ - case object Error extends LogLevel("error", 0) - - /** Log level corresponding to important notices or issues that are not - * severe. - */ - case object Warning extends LogLevel("warning", 1) - - /** Log level corresponding to usual information of what the application is - * doing. - */ - case object Info extends LogLevel("info", 2) - - /** Log level used for debugging the application. - * - * The messages can be more complex and targeted at developers diagnosing the - * application. - */ - case object Debug extends LogLevel("debug", 3) - - /** Log level used for advanced debugging, may be used for more throughout - * diagnostics. - */ - case object Trace extends LogLevel("trace", 4) - - /** Lists all available log levels. - * - * Can be used for example to automate parsing. - */ - val allLevels = Seq( - LogLevel.Off, - LogLevel.Error, - LogLevel.Warning, - LogLevel.Info, - LogLevel.Debug, - LogLevel.Trace - ) - - /** [[Ordering]] instance for [[LogLevel]]. - * - * The log levels are ordered from most severe. If a log level is enabled, it - * usually means that all levels smaller than it are enabled too. - */ - implicit val ord: Ordering[LogLevel] = (x, y) => x.level - y.level - - /** [[Encoder]] instance for [[LogLevel]]. */ - implicit val encoder: Encoder[LogLevel] = { - case Off => - throw new IllegalArgumentException( - "`None` log level should never be used in actual log messages and it " + - "cannot be serialized to prevent that." - ) - case level => - level.level.asJson - } - - /** [[Decoder]] instance for [[LogLevel]]. */ - implicit val decoder: Decoder[LogLevel] = { json => - json.as[Int].flatMap { level => - fromInteger(level).toRight( - DecodingFailure(s"`$level` is not a valid log level.", json.history) - ) - } - } - - /** Creates a [[LogLevel]] from its integer representation. - * - * Returns None if the number does not represent a valid log level. - */ - def fromInteger(level: Int): Option[LogLevel] = level match { - case Off.level => Some(Off) - case Error.level => Some(Error) - case Warning.level => Some(Warning) - case Info.level => Some(Info) - case Debug.level => Some(Debug) - case Trace.level => Some(Trace) - case _ => None - } - - /** Creates a [[LogLevel]] from its string representation. - * - * Returns None if the value does not represent a valid log level. - */ - def fromString(level: String): Option[LogLevel] = - level.toLowerCase match { - case Off.name => Some(Off) - case Error.name => Some(Error) - case Warning.name => Some(Warning) - case Info.name => Some(Info) - case Debug.name => Some(Debug) - case Trace.name => Some(Trace) - case _ => None - } - - /** Converts our internal [[LogLevel]] to the corresponding instance of - * Akka-specific log level. - */ - def toAkka(logLevel: LogLevel): akka.event.Logging.LogLevel = logLevel match { - case Off => akka.event.Logging.LogLevel(Int.MinValue) - case Error => akka.event.Logging.ErrorLevel - case Warning => akka.event.Logging.WarningLevel - case Info => akka.event.Logging.InfoLevel - case Debug => 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 - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/Logger.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/Logger.scala deleted file mode 100644 index 34a6219d288f..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/Logger.scala +++ /dev/null @@ -1,315 +0,0 @@ -package org.enso.loggingservice - -import org.enso.logger.masking.Masking -import org.enso.loggingservice.internal.{InternalLogMessage, LoggerConnection} -import org.slf4j.helpers.MessageFormatter -import org.slf4j.{Marker, Logger => SLF4JLogger} - -import scala.annotation.unused - -/** A [[SLF4JLogger]] instance for the SLF4J backend which passes all log - * messages to a [[LoggerConnection]]. - * - * @param name name of the logger - * @param connection the connection to pass the log messages to - * @param masking object that masks personally identifiable information - */ -class Logger( - name: String, - connection: LoggerConnection, - masking: Masking -) extends SLF4JLogger { - - /** @inheritdoc */ - override def getName: String = name - - private def isEnabled(level: LogLevel): Boolean = - connection.isEnabled(name, level) - - private def log( - level: LogLevel, - msg: String - ): Unit = { - if (isEnabled(level)) { - connection.send(InternalLogMessage(level, name, msg, None)) - } - } - - private def log( - level: LogLevel, - format: String, - arg: AnyRef - ): Unit = { - if (isEnabled(level)) { - val maskedArg = masking.mask(arg) - val fp = MessageFormatter.format(format, maskedArg) - connection.send( - InternalLogMessage(level, name, fp.getMessage, Option(fp.getThrowable)) - ) - } - } - - private def log( - level: LogLevel, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = { - if (isEnabled(level)) { - val maskedArg1 = masking.mask(arg1) - val maskedArg2 = masking.mask(arg2) - val fp = MessageFormatter.format(format, maskedArg1, maskedArg2) - connection.send( - InternalLogMessage(level, name, fp.getMessage, Option(fp.getThrowable)) - ) - } - } - - private def log( - level: LogLevel, - format: String, - args: Seq[AnyRef] - ): Unit = { - if (isEnabled(level)) { - val maskedArgs = args.map(masking.mask) - val fp = MessageFormatter.arrayFormat(format, maskedArgs.toArray) - connection.send( - InternalLogMessage(level, name, fp.getMessage, Option(fp.getThrowable)) - ) - } - } - - private def log( - level: LogLevel, - msg: String, - throwable: Throwable - ): Unit = { - if (isEnabled(level)) { - connection.send( - InternalLogMessage(level, name, msg, Some(throwable)) - ) - } - } - - override def isTraceEnabled: Boolean = isEnabled(LogLevel.Trace) - - override def trace(msg: String): Unit = log(LogLevel.Trace, msg) - - override def trace(format: String, arg: AnyRef): Unit = - log(LogLevel.Trace, format, arg) - - override def trace(format: String, arg1: AnyRef, arg2: AnyRef): Unit = - log(LogLevel.Trace, format, arg1, arg2) - - override def trace(format: String, arguments: AnyRef*): Unit = - log(LogLevel.Trace, format, arguments) - - override def trace(msg: String, t: Throwable): Unit = - log(LogLevel.Trace, msg, t) - - override def isTraceEnabled(@unused marker: Marker): Boolean = - isEnabled(LogLevel.Trace) - - override def trace(@unused marker: Marker, msg: String): Unit = - log(LogLevel.Trace, msg) - - override def trace( - @unused marker: Marker, - format: String, - arg: AnyRef - ): Unit = - log(LogLevel.Trace, format, arg) - - override def trace( - @unused marker: Marker, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = log(LogLevel.Trace, format, arg1, arg2) - - override def trace( - @unused marker: Marker, - format: String, - argArray: AnyRef* - ): Unit = - log(LogLevel.Trace, format, argArray) - - override def trace(@unused marker: Marker, msg: String, t: Throwable): Unit = - log(LogLevel.Trace, msg, t) - - override def isDebugEnabled: Boolean = isEnabled(LogLevel.Debug) - - override def debug(msg: String): Unit = log(LogLevel.Debug, msg) - - override def debug(format: String, arg: AnyRef): Unit = - log(LogLevel.Debug, format, arg) - - override def debug(format: String, arg1: AnyRef, arg2: AnyRef): Unit = - log(LogLevel.Debug, format, arg1, arg2) - - override def debug(format: String, arguments: AnyRef*): Unit = - log(LogLevel.Debug, format, arguments) - - override def debug(msg: String, t: Throwable): Unit = - log(LogLevel.Debug, msg, t) - - override def isDebugEnabled(@unused marker: Marker): Boolean = - isEnabled(LogLevel.Debug) - - override def debug(@unused marker: Marker, msg: String): Unit = - log(LogLevel.Debug, msg) - - override def debug( - @unused marker: Marker, - format: String, - arg: AnyRef - ): Unit = - log(LogLevel.Debug, format, arg) - - override def debug( - @unused marker: Marker, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = log(LogLevel.Debug, format, arg1, arg2) - - override def debug( - @unused marker: Marker, - format: String, - arguments: AnyRef* - ): Unit = - log(LogLevel.Debug, format, arguments) - - override def debug(@unused marker: Marker, msg: String, t: Throwable): Unit = - log(LogLevel.Debug, msg, t) - - override def isInfoEnabled: Boolean = isEnabled(LogLevel.Info) - - override def info(msg: String): Unit = log(LogLevel.Info, msg) - - override def info(format: String, arg: AnyRef): Unit = - log(LogLevel.Info, format, arg) - - override def info(format: String, arg1: AnyRef, arg2: AnyRef): Unit = - log(LogLevel.Info, format, arg1, arg2) - - override def info(format: String, arguments: AnyRef*): Unit = - log(LogLevel.Info, format, arguments) - - override def info(msg: String, t: Throwable): Unit = - log(LogLevel.Info, msg, t) - - override def isInfoEnabled(@unused marker: Marker): Boolean = - isEnabled(LogLevel.Info) - - override def info(@unused marker: Marker, msg: String): Unit = - log(LogLevel.Info, msg) - - override def info(@unused marker: Marker, format: String, arg: AnyRef): Unit = - log(LogLevel.Info, format, arg) - - override def info( - @unused marker: Marker, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = log(LogLevel.Info, format, arg1, arg2) - - override def info( - @unused marker: Marker, - format: String, - arguments: AnyRef* - ): Unit = - log(LogLevel.Info, format, arguments) - - override def info(@unused marker: Marker, msg: String, t: Throwable): Unit = - log(LogLevel.Info, msg, t) - - override def isWarnEnabled: Boolean = isEnabled(LogLevel.Warning) - - override def warn(msg: String): Unit = log(LogLevel.Warning, msg) - - override def warn(format: String, arg: AnyRef): Unit = - log(LogLevel.Warning, format, arg) - - override def warn(format: String, arguments: AnyRef*): Unit = - log(LogLevel.Warning, format, arguments) - - override def warn(format: String, arg1: AnyRef, arg2: AnyRef): Unit = - log(LogLevel.Warning, format, arg1, arg2) - - override def warn(msg: String, t: Throwable): Unit = - log(LogLevel.Warning, msg, t) - - override def isWarnEnabled(@unused marker: Marker): Boolean = - isEnabled(LogLevel.Warning) - - override def warn(@unused marker: Marker, msg: String): Unit = - log(LogLevel.Warning, msg) - - override def warn(@unused marker: Marker, format: String, arg: AnyRef): Unit = - log(LogLevel.Warning, format, arg) - - override def warn( - @unused marker: Marker, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = log(LogLevel.Warning, format, arg1, arg2) - - override def warn( - @unused marker: Marker, - format: String, - arguments: AnyRef* - ): Unit = - log(LogLevel.Warning, format, arguments) - - override def warn(@unused marker: Marker, msg: String, t: Throwable): Unit = - log(LogLevel.Warning, msg, t) - - override def isErrorEnabled: Boolean = isEnabled(LogLevel.Error) - - override def error(msg: String): Unit = log(LogLevel.Error, msg) - - override def error(format: String, arg: AnyRef): Unit = - log(LogLevel.Error, format, arg) - - override def error(format: String, arg1: AnyRef, arg2: AnyRef): Unit = - log(LogLevel.Error, format, arg1, arg2) - - override def error(format: String, arguments: AnyRef*): Unit = - log(LogLevel.Error, format, arguments) - - override def error(msg: String, t: Throwable): Unit = - log(LogLevel.Error, msg, t) - - override def isErrorEnabled(@unused marker: Marker): Boolean = - isEnabled(LogLevel.Error) - - override def error(@unused marker: Marker, msg: String): Unit = - log(LogLevel.Error, msg) - - override def error( - @unused marker: Marker, - format: String, - arg: AnyRef - ): Unit = - log(LogLevel.Error, format, arg) - - override def error( - @unused marker: Marker, - format: String, - arg1: AnyRef, - arg2: AnyRef - ): Unit = log(LogLevel.Error, format, arg1, arg2) - - override def error( - @unused marker: Marker, - format: String, - arguments: AnyRef* - ): Unit = - log(LogLevel.Error, format, arguments) - - override def error(@unused marker: Marker, msg: String, t: Throwable): Unit = - log(LogLevel.Error, msg, t) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerFactory.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerFactory.scala deleted file mode 100644 index da3b40a790ce..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerFactory.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.enso.loggingservice - -import org.enso.logger.masking.Masking -import org.slf4j.{ILoggerFactory, Logger => SLF4JLogger} - -/** A [[ILoggerFactory]] instance for the SLF4J backend. */ -class LoggerFactory extends ILoggerFactory { - - private val loggers = scala.collection.concurrent.TrieMap[String, Logger]() - - /** @inheritdoc */ - override def getLogger(name: String): SLF4JLogger = { - loggers.getOrElseUpdate( - name, { - val newLogger = - new Logger(name, LoggingServiceManager.Connection, Masking()) - if (!Masking.isMaskingEnabled) { - newLogger.warn("Log masking is disabled!") - } - newLogger - } - ) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerMode.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerMode.scala deleted file mode 100644 index 5d37f6fc2299..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggerMode.scala +++ /dev/null @@ -1,44 +0,0 @@ -package org.enso.loggingservice - -import akka.http.scaladsl.model.Uri -import org.enso.loggingservice.printers.Printer - -/** Represents modes the logging service can be running in. - * - * @tparam InitializationResult type that is returned when - * [[LoggingServiceManager]] sets up the given - * mode - */ -sealed trait LoggerMode[InitializationResult] -object LoggerMode { - - /** Forwards log messages to a logging service server. - * - * @param endpoint URI that is used to connect to the server via WebSockets - */ - case class Client(endpoint: Uri) extends LoggerMode[Unit] - - /** Starts gathering messages from this and other components. - * - * Its initialization returns a [[ServerBinding]] that can be used to connect - * to the initialized server. - * - * @param printers a list of printers that process the incoming messages - * @param port optional port to listen at, if not provided, the OS will - * choose a default port; the chosen port can be extracted from - * the [[ServerBinding]] that is returned when the service is - * initialized - * @param interface interface to listen at - */ - case class Server( - printers: Seq[Printer], - port: Option[Int] = None, - interface: String = "localhost" - ) extends LoggerMode[ServerBinding] - - /** Processes log messages locally. - * - * @param printers a list of printers that process the incoming messages - */ - case class Local(printers: Seq[Printer]) extends LoggerMode[Unit] -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceAlreadyInitializedException.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceAlreadyInitializedException.scala deleted file mode 100644 index 29ffeaef2552..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceAlreadyInitializedException.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.enso.loggingservice - -case class LoggingServiceAlreadyInitializedException() - extends RuntimeException( - "The logging service was already initialized. " + - "If it should be restarted, it should be torn down first." - ) diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceManager.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceManager.scala deleted file mode 100644 index 7a35b2c1d618..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceManager.scala +++ /dev/null @@ -1,195 +0,0 @@ -package org.enso.loggingservice - -import org.enso.loggingservice.internal._ -import org.enso.loggingservice.internal.service.{Client, Local, Server, Service} -import org.enso.loggingservice.printers.{Printer, StderrPrinter} - -import scala.concurrent.{ExecutionContext, Future} - -/** Manages the logging service. - */ -object LoggingServiceManager { - private var currentService: Option[Service] = None - private var currentLevel: LogLevel = LogLevel.Trace - - /** Returns the log level that is currently set up for the application. - * - * Its result can change depending on initialization state. - */ - def currentLogLevelForThisApplication(): LogLevel = currentLevel - - /** Creates an instance for the [[messageQueue]]. - * - * Runs special workaround logic if test mode is detected. - */ - private def initializeMessageQueue(): BlockingConsumerMessageQueue = { - LoggingSettings.testLogLevel match { - case Some(logLevel) => - val shouldOverride = () => currentService.isEmpty - System.err.println( - s"[Logging Service] Using test-mode logger at level $logLevel." - ) - new TestMessageQueue(logLevel, shouldOverride) - case None => - productionMessageQueue() - } - } - - private def productionMessageQueue() = new BlockingConsumerMessageQueue() - - private val messageQueue = initializeMessageQueue() - - /** The default [[LoggerConnection]] that should be used by all backends which - * want to use the logging service. - */ - object Connection extends LoggerConnection { - - /** @inheritdoc */ - override def send(message: InternalLogMessage): Unit = - messageQueue.send(Left(message)) - - /** @inheritdoc */ - override def logLevel: LogLevel = currentLevel - - /** @inheritdoc */ - override def loggers: Map[String, LogLevel] = - LoggingSettings.loggers - } - - /** Sets up the logging service, but in a separate thread to avoid stalling - * the application. - * - * The returned [[InitializationResult]] depends on the mode. - * - * It is important to note that any printers passed inside of `mode` are from - * now on owned by the setup function and the created service, so if service - * creation fails, they will be shutdown alongside service termination. Any - * printers passed to this function must not be reused. - * - * @param mode [[LoggerMode]] to setup - * @param logLevel specifies which log level should be used for logs from - * this instance; this log level does not affect remote log - * levels in server mode - * @param executionContext execution context to run the initialization in - * @return a future that will complete once the logger is initialized - */ - def setup[InitializationResult]( - mode: LoggerMode[InitializationResult], - logLevel: LogLevel - )(implicit - executionContext: ExecutionContext = - scala.concurrent.ExecutionContext.Implicits.global - ): Future[InitializationResult] = { - currentLevel = logLevel - Future(doSetup(mode, logLevel)) - } - - /** Shuts down the logging service if it was initialized or runs - * [[handlePendingMessages]] to handle logs that would be dropped due to the - * logging service never being initialized. - * - * This method is also called as a shutdown hook, but it is good to call it - * before shutting down to ensure that everything has a chance to terminate - * correctly before the application exits. It can be safely called multiple - * times. - */ - def tearDown(): Unit = { - val service = this.synchronized { - val service = currentService - currentService = None - service - } - - service match { - case Some(running) => running.terminate() - case None => - } - - handlePendingMessages() - } - - /** Checks if the logging service has been set up. */ - def isSetUp(): Boolean = this.synchronized { - currentService.isDefined - } - - Runtime.getRuntime.addShutdownHook(new Thread(() => tearDown())) - - /** Terminates the currently running logging service (if any) and replaces it - * with a fallback logging service. - * - * Can be used if the currently logging service fails after initialization - * and has to be shutdown. - */ - def replaceWithFallback( - printers: Seq[Printer] = Seq(StderrPrinter.create()) - ): Unit = { - val fallback = - Local.setup(currentLevel, messageQueue, printers) - val previousService = this.synchronized { - val previous = currentService - currentService = Some(fallback) - previous - } - - previousService match { - case Some(service) => - service.terminate() - case None => - } - } - - /** Removes any pending logs (so that [[handlePendingMessages]] will not print - * them). - * - * An internal method that is only used by [[TestLogger]]. - */ - def dropPendingLogs(): Unit = messageQueue.drain(LogLevel.Off) - - /** Prints any messages that have been buffered but have not been logged yet - * due to no loggers being active. - */ - private def handlePendingMessages(): Unit = { - val danglingMessages = messageQueue.drain(currentLevel) - if (danglingMessages.nonEmpty) { - InternalLogger.error( - "It seems that the logging service was never set up, " + - "or log messages were reported after it has been terminated. " + - "These messages are printed below:" - ) - val stderrPrinter = StderrPrinter.create() - danglingMessages.foreach { message => - stderrPrinter.print(message) - } - } - } - - private def doSetup[InitializationResult]( - mode: LoggerMode[InitializationResult], - logLevel: LogLevel - ): InitializationResult = { - this.synchronized { - if (currentService.isDefined) { - throw new LoggingServiceAlreadyInitializedException() - } - - val (service, result): (Service, InitializationResult) = mode match { - case LoggerMode.Client(endpoint) => - (Client.setup(endpoint, messageQueue, logLevel), ()) - case LoggerMode.Server(printers, port, interface) => - val server = Server.setup( - interface, - port.getOrElse(0), - messageQueue, - printers, - logLevel - ) - (server, server.getBinding()) - case LoggerMode.Local(printers) => - (Local.setup(logLevel, messageQueue, printers), ()) - } - currentService = Some(service) - result - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceSetupHelper.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceSetupHelper.scala deleted file mode 100644 index ed6a06f2de35..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/LoggingServiceSetupHelper.scala +++ /dev/null @@ -1,261 +0,0 @@ -package org.enso.loggingservice - -import akka.http.scaladsl.model.Uri -import com.typesafe.scalalogging.Logger -import org.enso.logger.masking.Masking -import org.enso.loggingservice.printers._ - -import java.nio.file.Path - -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, ExecutionContext, Future, Promise} -import scala.util.control.NonFatal -import scala.util.{Failure, Success} - -abstract class LoggingServiceSetupHelper(implicit - executionContext: ExecutionContext -) { - private val logger = Logger[this.type] - - /** Default log level to use if none is provided. */ - val defaultLogLevel: LogLevel - - /** The location for storing the log files. */ - def logPath: Path - - /** A suffix added to created log files. */ - val logFileSuffix: String - - /** Sets up the logging service as either a server that gathers other - * component's logs or a client that forwards them further. - * - * Forwarding logs to another server is currently an internal, - * development-mode feature that is not designed to be used by end-users - * unless they specifically know what they are doing. Redirecting logs to an - * external server may result in some important information not being printed - * by the application, being forwarded instead. - * - * @param logLevel the log level to use for this application's logs; does not - * affect other component's log level, which has to be set - * separately - * @param connectToExternalLogger specifies an Uri of an external logging - * service that the application should forward - * its logs to; advanced feature, use with - * caution - * @param colorMode specifies how to handle colors in console output - * @param logMasking switches the masking on and off - */ - def setup( - logLevel: Option[LogLevel], - connectToExternalLogger: Option[Uri], - colorMode: ColorMode, - logMasking: Boolean, - profilingLog: Option[Path] - ): Unit = { - val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - Masking.setup(logMasking) - connectToExternalLogger match { - case Some(uri) => - setupLoggingConnection(uri, actualLogLevel) - case None => - setupLoggingServer(actualLogLevel, colorMode, profilingLog) - } - } - - /** Sets up a fallback logger that just logs to stderr. - * - * It can be used when the application has failed to parse the CLI options - * and does not know which logger to set up. - */ - def setupFallback(): Unit = { - LoggingServiceManager - .setup( - LoggerMode.Local(Seq(fallbackPrinter)), - defaultLogLevel - ) - .onComplete { _ => - loggingServiceEndpointPromise.trySuccess(None) - } - } - - def fallbackPrinter: Printer = StderrPrinter.create(printExceptions = true) - - private val loggingServiceEndpointPromise = Promise[Option[Uri]]() - - /** Returns a [[Uri]] of the logging service that launched components can - * connect to. - * - * Points to the local server if it has been set up, or to the endpoint that - * the launcher was told to connect to. May be empty if the initialization - * failed and local logging is used as a fallback. - * - * The future is completed once the - */ - def loggingServiceEndpoint(): Future[Option[Uri]] = - loggingServiceEndpointPromise.future - - /** Returns a printer for outputting the logs to the standard error. */ - def stderrPrinter( - colorMode: ColorMode, - printExceptions: Boolean - ): Printer = - colorMode match { - case ColorMode.Never => - StderrPrinter.create(printExceptions) - case ColorMode.Auto => - StderrPrinterWithColors.colorPrinterIfAvailable(printExceptions) - case ColorMode.Always => - StderrPrinterWithColors.forceCreate(printExceptions) - } - - private def setupLoggingServer( - logLevel: LogLevel, - colorMode: ColorMode, - profilingLog: Option[Path] - ): Unit = { - val printExceptionsInStderr = - implicitly[Ordering[LogLevel]].compare(logLevel, LogLevel.Debug) >= 0 - - /** Creates a stderr printer and a file printer if a log file can be opened. - * - * This is a `def` on purpose, as even if the service fails, the printers - * are shut down, so the fallback must create new instances. - */ - def createPrinters() = - try { - val filePrinter = - FileOutputPrinter.create( - logDirectory = logPath, - suffix = logFileSuffix, - printExceptions = true - ) - val profilingPrinterOpt = profilingLog.map(new FileXmlPrinter(_)) - Seq( - stderrPrinter(colorMode, printExceptionsInStderr), - filePrinter - ) ++ profilingPrinterOpt - } catch { - case NonFatal(error) => - logger.error( - "Failed to initialize the write-to-file logger, " + - "falling back to stderr only.", - error - ) - Seq(stderrPrinter(colorMode, printExceptions = true)) - } - - LoggingServiceManager - .setup(LoggerMode.Server(createPrinters()), logLevel) - .onComplete { - case Failure(LoggingServiceAlreadyInitializedException()) => - logger.warn( - "Failed to initialize the logger because the logging service " + - "was already initialized." - ) - loggingServiceEndpointPromise.trySuccess(None) - case Failure(exception) => - logger.error( - s"Failed to initialize the logging service server: $exception", - exception - ) - logger.warn("Falling back to local-only logger.") - loggingServiceEndpointPromise.trySuccess(None) - LoggingServiceManager - .setup( - LoggerMode.Local(createPrinters()), - logLevel - ) - .onComplete { - case Failure(LoggingServiceAlreadyInitializedException()) => - logger.warn( - "Failed to initialize the fallback logger because the " + - "logging service was already initialized." - ) - loggingServiceEndpointPromise.trySuccess(None) - case Failure(fallbackException) => - System.err.println( - s"Failed to initialize the fallback logger: " + - s"$fallbackException" - ) - fallbackException.printStackTrace() - case Success(_) => - } - case Success(serverBinding) => - val uri = serverBinding.toUri() - try { - loggingServiceEndpointPromise.success(Some(uri)) - logger.trace( - s"Logging service has been set-up and is listening at `$uri`." - ) - } catch { - case _: IllegalStateException => - val earlierValue = loggingServiceEndpointPromise.future.value - logger.warn( - s"The logging service has been set-up at `$uri`, but the " + - s"logging URI has been initialized before that to " + - s"$earlierValue." - ) - } - } - } - - /** Connects this application to an external logging service. - * - * Currently, this is an internal function used mostly for testing purposes. - * It is not a user-facing API. - */ - private def setupLoggingConnection(uri: Uri, logLevel: LogLevel): Unit = { - LoggingServiceManager - .setup( - LoggerMode.Client(uri), - logLevel - ) - .map(_ => true) - .recoverWith { _ => - LoggingServiceManager - .setup( - LoggerMode.Local(Seq(fallbackPrinter)), - logLevel - ) - .map(_ => false) - } - .onComplete { - case Failure(exception) => - System.err.println(s"Failed to initialize the logger: $exception") - exception.printStackTrace() - loggingServiceEndpointPromise.trySuccess(None) - case Success(connected) => - if (connected) { - try { - loggingServiceEndpointPromise.success(Some(uri)) - System.err.println( - s"Log messages are forwarded to `$uri`." - ) - } catch { - case _: IllegalStateException => - val earlierValue = loggingServiceEndpointPromise.future.value - logger.warn( - s"The logging service has been set-up at `$uri`, but the " + - s"logging URI has been initialized before that to " + - s"$earlierValue." - ) - } - } else { - loggingServiceEndpointPromise.trySuccess(None) - } - } - } - - /** Waits until the logging service has been set-up. - * - * Due to limitations of how the logging service is implemented, it can only - * be terminated after it has been set up. - */ - def waitForSetup(): Unit = { - Await.ready(loggingServiceEndpointPromise.future, 5.seconds) - } - - /** Shuts down the logging service gracefully. - */ - def tearDown(): Unit = LoggingServiceManager.tearDown() -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ServerBinding.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ServerBinding.scala deleted file mode 100644 index 935405f36362..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/ServerBinding.scala +++ /dev/null @@ -1,13 +0,0 @@ -package org.enso.loggingservice - -import akka.http.scaladsl.model.Uri -import akka.http.scaladsl.model.Uri.{Authority, Host, Path} - -case class ServerBinding(port: Int) { - def toUri(host: String = "localhost"): Uri = - Uri( - scheme = "ws", - authority = Authority(host = Host(host), port = port), - path = Path./ - ) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/TestLogger.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/TestLogger.scala deleted file mode 100644 index 6bb6026575f4..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/TestLogger.scala +++ /dev/null @@ -1,54 +0,0 @@ -package org.enso.loggingservice - -import org.enso.loggingservice.printers.TestPrinter - -import scala.concurrent.Await -import scala.concurrent.duration.DurationInt - -/** A helper object for handling logs in tests. - */ -object TestLogger { - - /** A log message returned by [[gatherLogs]]. - * - * It contains the loglevel and message, but ignores attached exceptions. - */ - case class TestLogMessage(logLevel: LogLevel, message: String) - - /** Gathers logs logged during execution of `action`. - * - * This method should be used only inside of tests. Any tests using it should - * be ran with `parallelExecution` set to false, as global logger state has - * to be modified to gather the logs. - */ - def gatherLogs[R](action: => R): (R, Seq[TestLogMessage]) = { - LoggingServiceManager.dropPendingLogs() - if (LoggingServiceManager.isSetUp()) { - throw new IllegalStateException( - "gatherLogs called but another logging service has been already set " + - "up, this would lead to conflicts" - ) - } - val printer = new TestPrinter - val future = LoggingServiceManager.setup( - LoggerMode.Local(Seq(printer)), - LogLevel.Trace - ) - Await.ready(future, 1.second) - val result = action - Thread.sleep(100) - LoggingServiceManager.tearDown() - (result, printer.getLoggedMessages) - } - - /** Drops any logs that are pending due to the logging service not being set - * up. - * - * This method should be used only inside of tests. Any tests using it should - * be ran with `parallelExecution` set to false, as global logger state has - * to be modified to gather the logs. - */ - def dropLogs(): Unit = { - LoggingServiceManager.dropPendingLogs() - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/ANSIColorsMessageRenderer.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/ANSIColorsMessageRenderer.scala deleted file mode 100644 index b0a1cf1041a7..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/ANSIColorsMessageRenderer.scala +++ /dev/null @@ -1,28 +0,0 @@ -package org.enso.loggingservice.internal -import org.enso.loggingservice.LogLevel - -import scala.io.AnsiColor - -/** Renders log messages in the same way as [[DefaultLogMessageRenderer]] but - * adds ANSI escape codes to display the log level in color. - */ -class ANSIColorsMessageRenderer(printExceptions: Boolean) - extends DefaultLogMessageRenderer(printExceptions) { - - /** @inheritdoc - */ - override def renderLevel(logLevel: LogLevel): String = { - val color = logLevel match { - case LogLevel.Error => Some(AnsiColor.RED) - case LogLevel.Warning => Some(AnsiColor.YELLOW) - case LogLevel.Debug => Some(AnsiColor.CYAN) - case LogLevel.Trace => Some(AnsiColor.CYAN) - case _ => None - } - color match { - case Some(ansiColor) => - s"$ansiColor${super.renderLevel(logLevel)}${AnsiColor.RESET}" - case None => super.renderLevel(logLevel) - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/AnsiTerminal.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/AnsiTerminal.scala deleted file mode 100755 index 5282b0d9d2f8..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/AnsiTerminal.scala +++ /dev/null @@ -1,54 +0,0 @@ -package org.enso.loggingservice.internal - -import com.typesafe.scalalogging.Logger -import org.graalvm.nativeimage.ImageInfo - -/** Handles VT-compatible color output in the terminal. - */ -object AnsiTerminal { - - /** Tries enabling ANSI colors in terminal output and returns true if it - * succeeded. - * - * We assume that ANSI colors are supported by default on UNIX platforms. On - * Windows, we use native calls to enable them, currently this is only - * supported in native-image builds. Currently ANSI colors are not supported - * on non-native Windows targets. - */ - def tryEnabling(): Boolean = { - if (isWindows) { - if (ImageInfo.inImageCode) { - try { - NativeAnsiTerm.enableVT() - true - } catch { - case error: RuntimeException => - Logger[AnsiTerminal.type].warn( - s"Failed to initialize VT terminal (output will not contain " + - s"colors): ${error.getMessage}" - ) - false - } - } else false - } else true - } - - private def isWindows: Boolean = - System.getProperty("os.name").toLowerCase.contains("win") - - /** Checks if output of this program may be piped. - */ - def isLikelyPiped: Boolean = System.console() == null - - /** Checks if the output is connected to a terminal that can handle color - * output. - * - * On Windows, this function also enables color output, so any code that - * wants to use VT escape codes for colors (and is not assuming that its - * output is redirected) should first call this function to try enabling it - * and only use them if this function returned true. - */ - def canUseColors(): Boolean = { - !isLikelyPiped && AnsiTerminal.tryEnabling() - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BaseLogMessage.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BaseLogMessage.scala deleted file mode 100644 index a7e80040ef3e..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BaseLogMessage.scala +++ /dev/null @@ -1,15 +0,0 @@ -package org.enso.loggingservice.internal - -import java.time.Instant - -import org.enso.loggingservice.LogLevel - -/** A base type for log messages parametrized by the exception representation. - */ -trait BaseLogMessage[ExceptionType] { - def level: LogLevel - def timestamp: Instant - def group: String - def message: String - def exception: Option[ExceptionType] -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BlockingConsumerMessageQueue.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BlockingConsumerMessageQueue.scala deleted file mode 100644 index 1d0d2ee9ee70..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/BlockingConsumerMessageQueue.scala +++ /dev/null @@ -1,88 +0,0 @@ -package org.enso.loggingservice.internal - -import java.util.concurrent.ArrayBlockingQueue - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.WSLogMessage - -import scala.annotation.tailrec - -/** A message queue that can be consumed by a thread in a loop with a limited - * buffer. - */ -class BlockingConsumerMessageQueue(bufferSize: Int = 5000) { - - /** Enqueues the `message` to be sent and returns immediately. - * - * If any underlying buffers are full, they may be removed and a warning will - * be issued. - */ - def send(message: Either[InternalLogMessage, WSLogMessage]): Unit = { - val inserted = queue.offer(message) - if (!inserted) { - queue.clear() - queue.offer(Left(queueOverflowMessage)) - } - } - - /** Returns next message in the queue, skipping messages that should be - * ignored and waiting if no messages are currently available. - * - * The distinction between internal and external messages is that internal - * messages should only be considered if they have log level that is enabled. - * However, all external log messages should be processed, regardless of - * their log level, because external messages come from other components - * whose log level is set independently. - */ - @tailrec - final def nextMessage(internalLogLevel: LogLevel): WSLogMessage = { - val (message, internal) = encodeMessage(queue.take()) - if (isMessageRelevant(internalLogLevel)(message, internal)) - message - else nextMessage(internalLogLevel) - } - - /** Returns all currently enqueued messages, skipping ones that should be - * ignored. - * - * See [[nextMessage]] for explanation which messages are ignored. - */ - def drain(internalLogLevel: LogLevel): Seq[WSLogMessage] = { - val buffer = scala.collection.mutable - .Buffer[Either[InternalLogMessage, WSLogMessage]]() - import scala.jdk.CollectionConverters._ - queue.drainTo(buffer.asJava) - buffer.toSeq - .map(encodeMessage) - .filter((isMessageRelevant(internalLogLevel) _).tupled) - .map(_._1) - } - - /** All external messages are relevant, but internal messages relevancy depends - * on its log level. - */ - private def isMessageRelevant( - internalLogLevel: LogLevel - )(message: WSLogMessage, internal: Boolean): Boolean = - !internal || internalLogLevel.shouldLog(message.level) - - /** Returns the encoded message and a boolean value indicating if it was - * internal. - */ - private def encodeMessage( - message: Either[InternalLogMessage, WSLogMessage] - ): (WSLogMessage, Boolean) = - message.fold(msg => (msg.toLogMessage, true), (_, false)) - - private val queueOverflowMessage: InternalLogMessage = - InternalLogMessage( - level = LogLevel.Warning, - classOf[BlockingConsumerMessageQueue].getCanonicalName, - "The Logger does not keep up with processing log messages. " + - "Some log messages have been dropped.", - None - ) - - private val queue = - new ArrayBlockingQueue[Either[InternalLogMessage, WSLogMessage]](bufferSize) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/DefaultLogMessageRenderer.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/DefaultLogMessageRenderer.scala deleted file mode 100644 index fb95fccbfaef..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/DefaultLogMessageRenderer.scala +++ /dev/null @@ -1,75 +0,0 @@ -package org.enso.loggingservice.internal - -import java.time.format.DateTimeFormatter -import java.time.{Instant, ZoneOffset, ZonedDateTime} - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.{ - SerializedException, - WSLogMessage -} - -/** Renders the log message using the default format, including attached - * exceptions if [[printExceptions]] is set. - */ -class DefaultLogMessageRenderer(printExceptions: Boolean) - extends LogMessageRenderer { - - /** @inheritdoc - */ - override def render(logMessage: WSLogMessage): String = { - val level = renderLevel(logMessage.level) - val timestamp = renderTimestamp(logMessage.timestamp) - val base = - s"[$level] [$timestamp] [${logMessage.group}] ${logMessage.message}" - addException(base, logMessage.exception) - } - - private val timestampZone = ZoneOffset.UTC - - /** Renders the timestamp. - */ - def renderTimestamp(timestamp: Instant): String = - ZonedDateTime - .ofInstant(timestamp, timestampZone) - .format(DateTimeFormatter.ISO_ZONED_DATE_TIME) - - /** Adds attached exception's stack trace (if available) to the log if - * printing stack traces is enabled. - */ - def addException( - message: String, - exception: Option[SerializedException] - ): String = - exception match { - case Some(e) if printExceptions => - message + "\n" + renderException(e) - case _ => message - } - - /** Renders an exception with its strack trace. - */ - def renderException(exception: SerializedException): String = { - val head = s"${exception.name}: ${exception.message}" - val trace = exception.stackTrace.map(elem => - s" at ${elem.element}(${elem.location})" - ) - val cause = exception.cause - .map(e => s"\nCaused by: ${renderException(e)}") - .getOrElse("") - head + trace.map("\n" + _).mkString + cause - } - - /** Renders a log level. - */ - def renderLevel(logLevel: LogLevel): String = - logLevel match { - case LogLevel.Error => "error" - case LogLevel.Warning => "warn" - case LogLevel.Info => "info" - case LogLevel.Debug => "debug" - case LogLevel.Trace => "trace" - case LogLevel.Off => "off" - case _ => "error"; - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogMessage.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogMessage.scala deleted file mode 100644 index ae3083dcd711..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogMessage.scala +++ /dev/null @@ -1,57 +0,0 @@ -package org.enso.loggingservice.internal - -import java.time.Instant -import java.time.temporal.ChronoUnit - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.{ - SerializedException, - WSLogMessage -} - -/** The internal log message that is used for local logging. - * - * @param level log level - * @param timestamp timestamp indicating when the message was created - * @param group group associated with the message - * @param message text message - * @param exception optional attached exception - */ -case class InternalLogMessage( - level: LogLevel, - timestamp: Instant, - group: String, - message: String, - exception: Option[Throwable] -) extends BaseLogMessage[Throwable] { - - /** Converts to [[WSLogMessage]] by serializing the attached exception. - */ - def toLogMessage: WSLogMessage = - WSLogMessage( - level = level, - timestamp = timestamp.truncatedTo(ChronoUnit.MILLIS), - group = group, - message = message, - exception = exception.map(SerializedException.fromException) - ) -} - -object InternalLogMessage { - - /** Creates a log message with the timestamp set to the current instant. - */ - def apply( - level: LogLevel, - group: String, - message: String, - exception: Option[Throwable] - ): InternalLogMessage = - InternalLogMessage( - level = level, - timestamp = Instant.now(), - group = group, - message = message, - exception = exception - ) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogger.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogger.scala deleted file mode 100644 index d3d3f5fe7f59..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/InternalLogger.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.enso.loggingservice.internal - -/** An internal logger used for reporting errors within the logging service - * itself. - * - * As the logging service cannot be used to report its own errors (because a - * logging service error likely means that it is in a unusable state), its - * errors are printed to the standard error output. - */ -object InternalLogger { - - /** Reports an internal logging service error with the given message. - */ - def error(message: String): Unit = { - System.err.println(s"[internal-logger-error] $message") - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LogMessageRenderer.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LogMessageRenderer.scala deleted file mode 100644 index f67692e19cd0..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LogMessageRenderer.scala +++ /dev/null @@ -1,13 +0,0 @@ -package org.enso.loggingservice.internal - -import org.enso.loggingservice.internal.protocol.WSLogMessage - -/** Specifies a strategy of rendering log messages to string. - */ -trait LogMessageRenderer { - - /** Creates a string representation of the log message that can be written to - * an output. - */ - def render(logMessage: WSLogMessage): String -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggerConnection.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggerConnection.scala deleted file mode 100644 index 6c28abdc020d..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggerConnection.scala +++ /dev/null @@ -1,40 +0,0 @@ -package org.enso.loggingservice.internal - -import org.enso.loggingservice.LogLevel - -/** An interface that allows to send log messages to the logging service. */ -trait LoggerConnection { - - /** Sends a message to the logging service. - * - * It should return immediately. Sending a message usually means that it is - * enqueued and will be encoded and sent to the logging service soon, but it - * is possible for messages to be dropped if too many messages are logged in - * a short period of time. - */ - def send(message: InternalLogMessage): Unit - - /** Current log level. - * - * Only messages that have equal or smaller log level should be sent. Other - * messages will be ignored. - */ - def logLevel: LogLevel - - /** Extra logger settings overriding the default log level. - * - * @return a mapping from a logger name to the log level that will be used - * for that logger. - */ - def loggers: Map[String, LogLevel] - - /** Tells if messages with the provided log level should be sent. */ - def isEnabled(name: String, level: LogLevel): Boolean = { - val loggerLevel = - loggers - .find(entry => name.startsWith(entry._1)) - .map(_._2) - .getOrElse(logLevel) - implicitly[Ordering[LogLevel]].lteq(level, loggerLevel) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggingSettings.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggingSettings.scala deleted file mode 100644 index e218358eab5f..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/LoggingSettings.scala +++ /dev/null @@ -1,81 +0,0 @@ -package org.enso.loggingservice.internal - -import com.typesafe.config.{Config, ConfigFactory} -import org.enso.loggingservice.LogLevel - -import scala.collection.immutable.ListMap - -/** Reads logger settings from the resources. - * - * Currently these settings are used to configure logging inside of tests. - */ -object LoggingSettings { - - private object Key { - val root = "logging-service" - val logger = "logger" - val testLogLevel = "test-log-level" - val GLOB = "*" - } - - private lazy val configuration: Config = { - val empty = ConfigFactory.empty().atKey(Key.logger).atKey(Key.root) - ConfigFactory.load().withFallback(empty).getConfig(Key.root) - } - - /** Log level settings overriding the default application log level. - * - * @return a mapping from a logger name to the log level that will be used - * for that logger. - */ - lazy val loggers: Map[String, LogLevel] = { - def normalize(key: String): String = - key.replace("'", "").replace("\"", "") - - val loggerConfig = configuration.getConfig(Key.logger) - val builder = ListMap.newBuilder[String, LogLevel] - // `config` is unordered. To keep glob (*) entries at the end of the `ListMap`, - // gather them at the `fallback` map, and then append to the final `builder` - val fallback = ListMap.newBuilder[String, LogLevel] - - loggerConfig.entrySet.forEach { entry => - val key = entry.getKey - val value = loggerConfig.getString(key) - LogLevel.fromString(value) match { - case Some(logLevel) => - val normalizedKey = normalize(key) - if (normalizedKey.endsWith(Key.GLOB)) { - fallback += normalizedKey.dropRight(Key.GLOB.length + 1) -> logLevel - } else { - builder += normalizedKey -> logLevel - } - case None => - System.err.println( - s"Invalid log level for key [${normalize(key)}] set in " + - s"application config [$value]. Default log level will be used." - ) - } - } - - builder ++= fallback.result() - builder.result() - } - - /** Indicates the log level to be used in test mode. - * - * If set to None, production logging should be used. - */ - lazy val testLogLevel: Option[LogLevel] = { - Option.when(configuration.hasPath(Key.testLogLevel)) { - val value = configuration.getString(Key.testLogLevel) - LogLevel.fromString(value).getOrElse { - System.err.println( - s"Invalid log level for key [${Key.testLogLevel}] set in " + - s"application config [$value], falling back to info." - ) - LogLevel.Info - } - } - } - -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/TestMessageQueue.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/TestMessageQueue.scala deleted file mode 100644 index 6ca9ce2d8dcf..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/TestMessageQueue.scala +++ /dev/null @@ -1,35 +0,0 @@ -package org.enso.loggingservice.internal -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.printers.StderrPrinter - -/** A message queue for use in testing. - * - * It has a smaller buffer and ignores messages from a certain log level. - * - * @param logLevel specifies which messages will be printed to stderr if no - * service is set-up - * @param isLoggingServiceSetUp a function used to check if a logging service - * is set up - */ -class TestMessageQueue(logLevel: LogLevel, isLoggingServiceSetUp: () => Boolean) - extends BlockingConsumerMessageQueue(bufferSize = 100) { - - private def shouldKeepMessage( - message: Either[InternalLogMessage, WSLogMessage] - ): Boolean = message match { - case Left(value) => logLevel.shouldLog(value.level) - case Right(value) => logLevel.shouldLog(value.level) - } - - private val overridePrinter = StderrPrinter.create() - - /** @inheritdoc */ - override def send(message: Either[InternalLogMessage, WSLogMessage]): Unit = - if (isLoggingServiceSetUp()) { - if (shouldKeepMessage(message)) - overridePrinter.print(message.fold(_.toLogMessage, identity)) - } else { - super.send(message) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/SerializedException.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/SerializedException.scala deleted file mode 100644 index 8e2ca6783008..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/SerializedException.scala +++ /dev/null @@ -1,191 +0,0 @@ -package org.enso.loggingservice.internal.protocol - -import io.circe.syntax._ -import io.circe._ - -/** Represents a language-agnostic exception that can be sent over the WebSocket - * connection. - * - * @param name name of the exception, corresponds to the qualified class name - * of the exception - * @param message message as returned by the exception's getMessage method - * @param stackTrace serialized stack trace - * @param cause optional serialized exception that caused this one; it must not - * point to itself, as this structure cannot be cyclic so that it - * can be serialized into JSON - */ -case class SerializedException( - name: String, - message: Option[String], - stackTrace: Seq[SerializedException.TraceElement], - cause: Option[SerializedException] -) - -object SerializedException { - - /** Creates a [[SerializedException]] with a cause. - */ - def apply( - name: String, - message: String, - stackTrace: Seq[SerializedException.TraceElement], - cause: SerializedException - ): SerializedException = - SerializedException( - name = name, - message = Some(message), - stackTrace = stackTrace, - cause = Some(cause) - ) - - /** Creates a [[SerializedException]] without a cause. - */ - def apply( - name: String, - message: String, - stackTrace: Seq[SerializedException.TraceElement] - ): SerializedException = - new SerializedException( - name = name, - message = Some(message), - stackTrace = stackTrace, - cause = None - ) - - /** Encodes a JVM [[Throwable]] as [[SerializedException]]. - */ - def fromException(throwable: Throwable): SerializedException = { - val clazz = throwable.getClass - val cause = - if (throwable.getCause == throwable) None - else Option(throwable.getCause).map(fromException) - SerializedException( - name = Option(clazz.getCanonicalName).getOrElse(clazz.getName), - message = Option(throwable.getMessage), - stackTrace = throwable.getStackTrace.toSeq.map(encodeStackTraceElement), - cause = cause - ) - } - - /** Regular expression used to parse the stack trace elements format used by - * [[StackTraceElement#toString]]. - * - * It assumes that the stack trace element has form `element(location)`, for - * example `foo.bar(Foo.java:123)` or `win32.foo(Native Method)`. - * - * This is the most robust way to get the location from the - * [[StackTraceElement]] without duplicating the standard library code. In - * case that the result of [[StackTraceElement#toString]] does not match the - * regex, an approximation based on its getters is used. - */ - private val stackTraceRegex = "(.*)\\((.*)\\)".r - - /** Encodes a [[StackTraceElement]] as [[TraceElement]]. - */ - private def encodeStackTraceElement( - stackTraceElement: StackTraceElement - ): TraceElement = { - stackTraceElement.toString match { - case stackTraceRegex(element, location) => - TraceElement(element = element, location = location) - case _ => - val location = for { - filename <- Option(stackTraceElement.getFileName) - line <- - if (stackTraceElement.getLineNumber < 0) None - else Some(stackTraceElement.getLineNumber) - } yield s"$filename:$line" - val className = stackTraceElement.getClassName - val methodName = stackTraceElement.getMethodName - TraceElement( - s"$className.$methodName", - location.getOrElse("Unknown Source") - ) - } - } - - /** Represents an element of a stack trace attached to the - * [[SerializedException]]. - * - * @param element name of the stack location; for example, in Java this is - * the qualified method name - * @param location code location of the element - */ - case class TraceElement(element: String, location: String) - - private object JsonFields { - val Name = "name" - val Message = "message" - val StackTrace = "trace" - val Cause = "cause" - - object TraceElement { - val Element = "element" - val Location = "location" - } - } - - /** [[Encoder]] instance for [[SerializedException]]. - */ - implicit val encoder: Encoder[SerializedException] = encodeException - - /** Encodes a [[SerializedException]] as its JSON representation. - */ - private def encodeException(exception: SerializedException): Json = { - val base = JsonObject( - JsonFields.Name -> exception.name.asJson, - JsonFields.Message -> exception.message.asJson, - JsonFields.StackTrace -> exception.stackTrace.asJson - ) - - val result = exception.cause match { - case Some(cause) => - base.+:((JsonFields.Cause, encodeException(cause))) - case None => - base - } - - result.asJson - } - - /** [[Decoder]] instance for [[SerializedException]]. - */ - implicit def decoder: Decoder[SerializedException] = decodeException - - /** Tries to decode a [[SerializedException]] from its JSON representation. - */ - private def decodeException( - json: HCursor - ): Decoder.Result[SerializedException] = { - for { - name <- json.get[String](JsonFields.Name) - message <- json.get[Option[String]](JsonFields.Message) - stackTrace <- json.get[Seq[TraceElement]](JsonFields.StackTrace) - cause <- - json.getOrElse[Option[SerializedException]](JsonFields.Cause)(None) - } yield SerializedException( - name = name, - message = message, - stackTrace = stackTrace, - cause = cause - ) - } - - /** [[Encoder]] instance for [[TraceElement]]. - */ - implicit val traceEncoder: Encoder[TraceElement] = { traceElement => - Json.obj( - JsonFields.TraceElement.Element -> traceElement.element.asJson, - JsonFields.TraceElement.Location -> traceElement.location.asJson - ) - } - - /** [[Decoder]] instance for [[TraceElement]]. - */ - implicit val traceDecoder: Decoder[TraceElement] = { json => - for { - element <- json.get[String](JsonFields.TraceElement.Element) - location <- json.get[String](JsonFields.TraceElement.Location) - } yield TraceElement(element = element, location = location) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/WSLogMessage.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/WSLogMessage.scala deleted file mode 100644 index 1a733335d690..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/protocol/WSLogMessage.scala +++ /dev/null @@ -1,80 +0,0 @@ -package org.enso.loggingservice.internal.protocol - -import java.time.Instant - -import io.circe.syntax._ -import io.circe.{Decoder, Encoder, JsonObject} -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.BaseLogMessage - -/** The encoded log message that can be sent over the WebSocket connection or - * passed to a printer. - * - * @param level log level - * @param timestamp timestamp indicating when the message was created - * @param group group associated with the message - * @param message text message - * @param exception optional serialized exception attached to the message - */ -case class WSLogMessage( - level: LogLevel, - timestamp: Instant, - group: String, - message: String, - exception: Option[SerializedException] -) extends BaseLogMessage[SerializedException] - -object WSLogMessage { - private object JsonFields { - val Level = "level" - val Timestamp = "time" - val Group = "group" - val Message = "message" - val Exception = "exception" - } - - /** [[Encoder]] instance for [[WSLogMessage]]. - */ - implicit val encoder: Encoder[WSLogMessage] = { message => - var base = JsonObject( - JsonFields.Level -> message.level.asJson, - JsonFields.Timestamp -> message.timestamp.toEpochMilli.asJson - ) - - if (message.group != null) { - base = base.+:((JsonFields.Group -> message.group.asJson)) - } - if (message.message != null) { - base = base.+:((JsonFields.Message -> message.message.asJson)) - } - - val result = message.exception match { - case Some(exception) => - base.+:((JsonFields.Exception, exception.asJson)) - case None => - base - } - - result.asJson - } - - /** [[Decoder]] instance for [[WSLogMessage]]. - */ - implicit val decoder: Decoder[WSLogMessage] = { json => - for { - level <- json.get[LogLevel](JsonFields.Level) - timestamp <- - json.get[Long](JsonFields.Timestamp).map(Instant.ofEpochMilli) - group <- json.get[String](JsonFields.Group) - message <- json.get[String](JsonFields.Message) - exception <- - json.getOrElse[Option[SerializedException]](JsonFields.Exception)(None) - } yield WSLogMessage( - level = level, - timestamp = timestamp, - group = group, - message = message, - exception = exception - ) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Client.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Client.scala deleted file mode 100644 index b7ed6547a96b..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Client.scala +++ /dev/null @@ -1,169 +0,0 @@ -package org.enso.loggingservice.internal.service - -import akka.Done -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.ws.{Message, TextMessage, WebSocketRequest} -import akka.http.scaladsl.model.{StatusCodes, Uri} -import akka.stream.scaladsl.{Keep, Sink, Source, SourceQueueWithComplete} -import akka.stream.{OverflowStrategy, QueueOfferResult} -import io.circe.syntax._ -import org.enso.loggingservice.internal.{ - BlockingConsumerMessageQueue, - InternalLogger -} -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.{LogLevel, LoggingServiceManager} - -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, Future} - -/** A client [[Service]] that passes incoming log messages to a server. - * - * @param serverUri uri of the server to connect to - * @param queue log message queue - * @param logLevel log level used to filter messages - */ -class Client( - serverUri: Uri, - protected val queue: BlockingConsumerMessageQueue, - protected val logLevel: LogLevel -) extends ThreadProcessingService - with ServiceWithActorSystem { - - /** @inheritdoc - */ - override protected def actorSystemName: String = "logging-service-client" - - /** Starts the client service by trying to connect to the server. - * - * Returns a future that is completed once the connection has been - * established. - */ - def start(): Future[Unit] = { - val request = WebSocketRequest(serverUri) - val flow = Http().webSocketClientFlow(request) - val incoming = Sink.ignore - val outgoing = Source.queue[Message](0, OverflowStrategy.backpressure) - - val ((outgoingQueue, upgradeResponse), closed) = - outgoing.viaMat(flow)(Keep.both).toMat(incoming)(Keep.both).run() - - import actorSystem.dispatcher - val connected = upgradeResponse.flatMap { upgrade => - if (upgrade.response.status == StatusCodes.SwitchingProtocols) { - Future.successful(Done) - } else { - throw new RuntimeException( - s"Connection failed: ${upgrade.response.status}" - ) - } - } - - connected.map(_ => { - closedConnection = Some(closed) - webSocketQueue = Some(outgoingQueue) - - closed.onComplete(_ => onDisconnected()) - - startQueueProcessor() - }) - } - - private var webSocketQueue: Option[SourceQueueWithComplete[Message]] = None - private var closedConnection: Option[Future[Done]] = None - - /** Tries to send the log message to the server by appending it to the queue - * of outgoing messages. - * - * It waits for the offer to complete for a long time to handle the case in - * which the server is unresponsive for a longer time. Any pending messages - * are just enqueued onto the main [[BlockingConsumerMessageQueue]] and will - * be sent after this one. - */ - override protected def processMessage(message: WSLogMessage): Unit = { - val queue = webSocketQueue.getOrElse( - throw new IllegalStateException( - "Internal error: The queue processor thread has been started before " + - "the connection has been initialized." - ) - ) - val serializedMessage = message.asJson.noSpaces - val offerResult = queue.offer(TextMessage.Strict(serializedMessage)) - try { - Await.result(offerResult, 30.seconds) match { - case QueueOfferResult.Enqueued => - case QueueOfferResult.Dropped => - InternalLogger.error(s"A log message has been dropped unexpectedly.") - case QueueOfferResult.Failure(cause) => - InternalLogger.error(s"A log message could not be sent: $cause.") - case QueueOfferResult.QueueClosed => throw new InterruptedException - } - } catch { - case _: concurrent.TimeoutException => - InternalLogger.error( - s"Adding a log message timed out. Messages may or may not be dropped." - ) - } - } - - @volatile private var shuttingDown: Boolean = false - - /** If the remote server closes the connection, notifies the logging service - * to start the fallback logger. - */ - private def onDisconnected(): Unit = { - if (!shuttingDown) { - InternalLogger.error( - "Server has disconnected, logging is falling back to stderr." - ) - LoggingServiceManager.replaceWithFallback() - } - } - - /** Closes the connection. - */ - override protected def terminateUser(): Future[_] = { - shuttingDown = true - webSocketQueue match { - case Some(wsQueue) => - import actorSystem.dispatcher - val promise = concurrent.Promise[Done]() - wsQueue.complete() - wsQueue.watchCompletion().onComplete(promise.tryComplete) - closedConnection.foreach(_.onComplete(promise.tryComplete)) - promise.future - case None => Future.successful(Done) - } - } - - /** No additional actions are performed after the thread has been shut down as - * termination happens in [[terminateUser()]]. - */ - override protected def afterShutdown(): Unit = {} -} - -object Client { - - /** Waits for the [[Client]] to start up and returns it or throws an exception - * on setup failure. - * - * @param serverUri uri of the server to connect to - * @param queue log message queue - * @param logLevel log level used to filter messages - */ - def setup( - serverUri: Uri, - queue: BlockingConsumerMessageQueue, - logLevel: LogLevel - ): Client = { - val client = new Client(serverUri, queue, logLevel) - try { - Await.result(client.start(), 3.seconds) - client - } catch { - case e: Throwable => - client.terminate() - throw e - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Local.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Local.scala deleted file mode 100644 index 66bbe50299e3..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Local.scala +++ /dev/null @@ -1,49 +0,0 @@ -package org.enso.loggingservice.internal.service - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.BlockingConsumerMessageQueue -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.printers.Printer - -/** A local [[Service]] that handles all log messages locally. - * - * @param logLevel log level used to filter messages - * @param queue log message queue - * @param printers printers defining handling of the log messages - */ -case class Local( - logLevel: LogLevel, - queue: BlockingConsumerMessageQueue, - printers: Seq[Printer] -) extends ThreadProcessingService { - - /** Passes each message to all printers. - */ - override protected def processMessage(message: WSLogMessage): Unit = - printers.foreach(_.print(message)) - - /** Shuts down the printers. - */ - override protected def afterShutdown(): Unit = { - printers.foreach(_.shutdown()) - } -} - -object Local { - - /** Starts the [[Local]] service and returns it. - * - * @param logLevel log level used to filter messages - * @param queue log message queue - * @param printers printers defining handling of the log messages - */ - def setup( - logLevel: LogLevel, - queue: BlockingConsumerMessageQueue, - printers: Seq[Printer] - ): Local = { - val local = new Local(logLevel, queue, printers) - local.startQueueProcessor() - local - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Server.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Server.scala deleted file mode 100644 index c1b942d358be..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Server.scala +++ /dev/null @@ -1,174 +0,0 @@ -package org.enso.loggingservice.internal.service - -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.AttributeKeys -import akka.http.scaladsl.model.HttpMethods._ -import akka.http.scaladsl.model.ws.{BinaryMessage, Message, TextMessage} -import akka.http.scaladsl.model.{HttpRequest, HttpResponse, Uri} -import akka.stream.scaladsl.{Flow, Sink, Source} -import io.circe.{parser, Error} -import org.enso.loggingservice.internal.{ - BlockingConsumerMessageQueue, - InternalLogger -} -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.printers.Printer -import org.enso.loggingservice.{LogLevel, ServerBinding} - -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, Future} - -/** A server [[Service]] which handles both messages incoming from a WebSocket - * connection and local log messages. - * - * @param interface interface to bind to - * @param port port to bind to; if set to 0, the system will allocate some - * available port - * @param queue log message queue - * @param printers printers defining handling of the log messages - * @param logLevel log level used to filter the local messages (messages from - * clients are passed as-is as they may use different log - * levels) - */ -class Server( - interface: String, - port: Int, - queue: BlockingConsumerMessageQueue, - printers: Seq[Printer], - logLevel: LogLevel -) extends Local(logLevel, queue, printers) - with ServiceWithActorSystem { - - /** @inheritdoc - */ - override protected def actorSystemName: String = "logging-service-server" - - /** Immediately starts processing local messages and returns a [[Future]] that - * will complete once the server has been started. - */ - def start(): Future[Unit] = { - startQueueProcessor() - startWebSocketServer() - } - - /** Starts the WebSocket server. - */ - private def startWebSocketServer(): Future[Unit] = { - val requestHandler: HttpRequest => HttpResponse = { - case req @ HttpRequest(GET, Uri.Path("/"), _, _, _) => - req.attribute(AttributeKeys.webSocketUpgrade) match { - case Some(upgrade) => - val flow = Flow.fromSinkAndSourceCoupled( - createMessageProcessor(), - Source.never - ) - upgrade.handleMessages(flow) - case None => - HttpResponse(400, entity = "Not a valid websocket request!") - } - case r: HttpRequest => - r.discardEntityBytes() - HttpResponse(404, entity = "Unknown resource!") - } - - import actorSystem.dispatcher - Http() - .newServerAt(interface, port) - .bindSync(requestHandler) - .map { serverBinding => - bindingOption = Some(serverBinding) - } - } - - /** Returns the binding that describes how to connect to the started server. - * - * This method can only be called after the future returned from [[start]] - * has completed. - */ - def getBinding(): ServerBinding = { - val binding = bindingOption.getOrElse( - throw new IllegalStateException( - "Binding requested before the server has been initialized." - ) - ) - - ServerBinding(port = binding.localAddress.getPort) - } - - private var bindingOption: Option[Http.ServerBinding] = None - - /** Creates a separate message processor for each connection. - * - * Each connection will only report the first invalid message, all further - * invalid messages are silently ignored. - */ - private def createMessageProcessor() = { - @volatile var invalidWasReported: Boolean = false - def reportInvalidMessage(error: Throwable): Unit = { - if (!invalidWasReported) { - InternalLogger.error(s"Invalid message: $error.") - invalidWasReported = true - } - } - Sink.foreach[Message] { - case tm: TextMessage => - val rawMessage = tm.textStream.fold("")(_ + _) - val decodedMessage = rawMessage.map(decodeMessage) - decodedMessage.runForeach { - case Left(error) => reportInvalidMessage(error) - case Right(message) => queue.send(Right(message)) - } - case bm: BinaryMessage => - reportInvalidMessage( - new IllegalStateException("Unexpected binary message.") - ) - bm.dataStream.runWith(Sink.ignore) - } - } - - private def decodeMessage(message: String): Either[Error, WSLogMessage] = - parser.parse(message).flatMap(_.as[WSLogMessage]) - - /** Shuts down the server. - */ - override protected def terminateUser(): Future[_] = { - bindingOption match { - case Some(binding) => - binding.terminate(hardDeadline = 2.seconds) - case None => Future.successful(()) - } - } -} - -object Server { - - /** Waits for the [[Server]] to start up and returns it or throws an exception - * on setup failure. - * - * @param interface interface to bind to - * @param port port to bind to; if set to 0, the system will allocate some - * available port - * @param queue log message queue - * @param printers printers defining handling of the log messages - * @param logLevel log level used to filter the local messages (messages from - * clients are passed as-is as they may use different log - * levels) - */ - def setup( - interface: String, - port: Int, - queue: BlockingConsumerMessageQueue, - printers: Seq[Printer], - logLevel: LogLevel - ): Server = { - val server = new Server(interface, port, queue, printers, logLevel) - try { - Await.result(server.start(), 3.seconds) - server - } catch { - case e: Throwable => - server.terminate() - throw e - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Service.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Service.scala deleted file mode 100644 index d7298521d9c7..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/Service.scala +++ /dev/null @@ -1,10 +0,0 @@ -package org.enso.loggingservice.internal.service - -/** A service backend that is used to process incoming log messages. - */ -trait Service { - - /** Terminates the service and releases its resources. - */ - def terminate(): Unit = {} -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ServiceWithActorSystem.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ServiceWithActorSystem.scala deleted file mode 100644 index 2a592db65833..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ServiceWithActorSystem.scala +++ /dev/null @@ -1,121 +0,0 @@ -package org.enso.loggingservice.internal.service - -import akka.actor.ActorSystem -import com.typesafe.config.{ConfigFactory, ConfigValueFactory} -import org.enso.loggingservice.internal.InternalLogger - -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, Future} - -/** A mix-in for implementing services that use an Akka [[ActorSystem]]. - */ -trait ServiceWithActorSystem extends Service { - - /** Name to use for the [[ActorSystem]]. - */ - protected def actorSystemName: String - - /** The [[ActorSystem]] that can be used by the service. - */ - implicit protected val actorSystem: ActorSystem = - initializeActorSystemForLoggingService(actorSystemName) - - /** Initializes an [[ActorSystem]], overriding the default logging settings. - * - * The Actor System responsible for the logging service cannot use the - * default configured logger, because this logger is likely to be the one - * bound to the logging service itself. The logging service cannot use itself - * for logging, because if it failed, it could not log its own failure or - * there could be a risk of entering an infinite loop if writing a log - * message triggered another log message. - * - * To avoid these issues, the Actor System responsible for the logging - * service overrides its logger setting to use the default standard output - * logger and is configured to only log warnings or errors. - */ - private def initializeActorSystemForLoggingService( - name: String - ): ActorSystem = { - import scala.jdk.CollectionConverters._ - val loggers: java.lang.Iterable[String] = - Seq("akka.event.Logging$StandardOutLogger").asJava - val config = { - val baseConfig = ConfigFactory - .empty() - .withValue("akka.loggers", ConfigValueFactory.fromAnyRef(loggers)) - .withValue( - "akka.logging-filter", - ConfigValueFactory.fromAnyRef("akka.event.DefaultLoggingFilter") - ) - .withValue("akka.loglevel", ConfigValueFactory.fromAnyRef("WARNING")) - .withValue( - "akka.coordinated-shutdown.run-by-actor-system-terminate", - ConfigValueFactory.fromAnyRef("off") - ) - .withValue("akka.daemonic", ConfigValueFactory.fromAnyRef("on")) - .withValue( - "akka.http.server.websocket.periodic-keep-alive-mode", - ConfigValueFactory.fromAnyRef("ping") - ) - .withValue( - "akka.http.server.websocket.periodic-keep-alive-max-idle", - ConfigValueFactory.fromAnyRef("30 seconds") - ) - - val timeouts = Seq( - "akka.http.server.idle-timeout", - "akka.http.client.idle-timeout", - "akka.http.host-connection-pool.client.idle-timeout", - "akka.http.host-connection-pool.idle-timeout" - ) - val configWithTimeouts = timeouts.foldLeft(baseConfig) { - case (config, key) => - config.withValue(key, ConfigValueFactory.fromAnyRef("120 seconds")) - } - - configWithTimeouts - } - - ActorSystem( - name, - config, - classLoader = - classOf[Server].getClassLoader // Note [Actor System Class Loader] - ) - } - - /** Called before terminating the [[ActorSystem]], can be used to handle any - * actions that should happen before it is terminated. - * - * The actor system will wait with its termination until the returned future - * completes. - */ - protected def terminateUser(): Future[_] - - /** Waits for up to 3 seconds for the [[terminateUser]] and [[ActorSystem]] - * termination, then handles any other termination logic. - */ - abstract override def terminate(): Unit = { - import actorSystem.dispatcher - val termination = terminateUser().map(_ => { - actorSystem.terminate() - }) - try { - Await.result(termination, 3.seconds) - } catch { - case _: concurrent.TimeoutException => - InternalLogger.error("The actor system did not terminate in time.") - } finally { - super.terminate() - } - } -} - -/* Note [Actor System Class Loader] - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Without explicitly setting the ClassLoader, the ActorSystem initialization - * fails (at least if run in `sbt`) with `java.lang.ClassCastException: - * interface akka.event.LoggingFilter is not assignable from class - * akka.event.DefaultLoggingFilter` which is most likely caused by the two - * instances coming from distinct class loaders. - */ diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ThreadProcessingService.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ThreadProcessingService.scala deleted file mode 100644 index 9976878ec196..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/internal/service/ThreadProcessingService.scala +++ /dev/null @@ -1,98 +0,0 @@ -package org.enso.loggingservice.internal.service - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.internal.{ - BlockingConsumerMessageQueue, - DefaultLogMessageRenderer, - InternalLogger -} - -import scala.util.control.NonFatal - -/** A mix-in for implementing services that process messages from a - * [[BlockingConsumerMessageQueue]] in a separate thread. - */ -trait ThreadProcessingService extends Service { - - /** The queue that is the source of messages. - */ - protected def queue: BlockingConsumerMessageQueue - - /** Log level used for filtering messages from the queue. - * @return - */ - protected def logLevel: LogLevel - - /** Logic responsible for processing each message from [[queue]]. - * - * This function is guaranteed to be called synchronously from a single - * thread. - */ - protected def processMessage(message: WSLogMessage): Unit - - /** Called after the message processing thread has been stopped, can be used - * to finish termination. - */ - protected def afterShutdown(): Unit - - private var queueThread: Option[Thread] = None - - /** Starts the thread processing messages from [[queue]]. - */ - protected def startQueueProcessor(): Unit = { - if (queueThread.isDefined) { - throw new IllegalStateException( - "The processing thread has already been started." - ) - } - - val thread = new Thread(() => runQueue()) - thread.setName("logging-service-processing-thread") - thread.setDaemon(true) - queueThread = Some(thread) - thread.start() - } - - private lazy val renderer = new DefaultLogMessageRenderer( - printExceptions = false - ) - - /** The runner filters out internal messages that have disabled log levels, - * but passes through all external messages (as their log level is set - * independently and can be lower). - */ - private def runQueue(): Unit = { - try { - while (!Thread.currentThread().isInterrupted) { - val message = queue.nextMessage(logLevel) - try { - processMessage(message) - } catch { - case NonFatal(e) => - InternalLogger.error( - s"One of the printers failed to write a message: $e" - ) - InternalLogger.error( - s"The dropped message was: ${renderer.render(message)}" - ) - } - } - } catch { - case _: InterruptedException => - } - } - - /** @inheritdoc */ - abstract override def terminate(): Unit = { - super.terminate() - queueThread match { - case Some(thread) => - thread.interrupt() - thread.join(100) - queueThread = None - afterShutdown() - case None => - } - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileOutputPrinter.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileOutputPrinter.scala deleted file mode 100644 index 6b7716eafe3a..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileOutputPrinter.scala +++ /dev/null @@ -1,76 +0,0 @@ -package org.enso.loggingservice.printers - -import java.io.PrintWriter -import java.nio.file.{Files, Path, StandardOpenOption} -import java.time.format.DateTimeFormatter -import java.time.{Instant, LocalDateTime, ZoneId} - -import org.enso.loggingservice.internal.DefaultLogMessageRenderer -import org.enso.loggingservice.internal.protocol.WSLogMessage - -/** Creates a new file in [[logDirectory]] and writes incoming log messages to - * this file. - * - * @param logDirectory the directory to create the logfile in - * @param suffix a suffix to be added to the filename - * @param printExceptions whether to print exceptions attached to the log - * messages - */ -class FileOutputPrinter( - logDirectory: Path, - suffix: String, - printExceptions: Boolean -) extends Printer { - - private val renderer = new DefaultLogMessageRenderer(printExceptions) - private val writer = initializeWriter() - - /** @inheritdoc */ - override def print(message: WSLogMessage): Unit = { - val lines = renderer.render(message) - writer.println(lines) - // TODO [RW] we may consider making flushing configurable as it is mostly - // useful for debugging crashes, whereas for usual usecases buffering could - // give slightly better performance - writer.flush() - } - - /** @inheritdoc */ - override def shutdown(): Unit = { - writer.flush() - writer.close() - } - - /** Opens the log file for writing. */ - private def initializeWriter(): PrintWriter = { - val logPath = logDirectory.resolve(makeLogFilename()) - Files.createDirectories(logDirectory) - new PrintWriter( - Files.newBufferedWriter( - logPath, - StandardOpenOption.CREATE_NEW, - StandardOpenOption.WRITE - ) - ) - } - - /** Creates a log filename that is created based on the current timestamp. */ - private def makeLogFilename(): String = { - val timestampZone = ZoneId.of("UTC") - val timestamp = LocalDateTime - .ofInstant(Instant.now(), timestampZone) - .format(DateTimeFormatter.ofPattern("YYYYMMdd-HHmmss-SSS")) - s"$timestamp-$suffix.log" - } -} - -object FileOutputPrinter { - - /** Creates a new [[FileOutputPrinter]]. */ - def create( - logDirectory: Path, - suffix: String, - printExceptions: Boolean - ): FileOutputPrinter = - new FileOutputPrinter(logDirectory, suffix, printExceptions) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileXmlPrinter.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileXmlPrinter.scala deleted file mode 100644 index 575ca1d86e8f..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/FileXmlPrinter.scala +++ /dev/null @@ -1,61 +0,0 @@ -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 = - "" -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/Printer.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/Printer.scala deleted file mode 100644 index f8b8857d0144..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/Printer.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.enso.loggingservice.printers - -import org.enso.loggingservice.internal.protocol.WSLogMessage - -/** Defines a strategy for outputting the log messages. - * - * It can output them to the console, a file, a database etc. - */ -trait Printer { - - /** Outputs the log message. - */ - def print(message: WSLogMessage): Unit - - /** Shuts down this output channel. - * - * It should flush any buffers and release resources. - */ - def shutdown(): Unit -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinter.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinter.scala deleted file mode 100644 index bc86b02ea1c3..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinter.scala +++ /dev/null @@ -1,32 +0,0 @@ -package org.enso.loggingservice.printers - -import org.enso.loggingservice.internal.DefaultLogMessageRenderer -import org.enso.loggingservice.internal.protocol.WSLogMessage - -/** Prints the log messages to the standard error output. - * - * @param printExceptions specifies if attached exceptions should be printed - */ -class StderrPrinter(printExceptions: Boolean) extends Printer { - private val renderer = new DefaultLogMessageRenderer(printExceptions) - - /** @inheritdoc - */ - override def print(logMessage: WSLogMessage): Unit = { - val lines = renderer.render(logMessage) - System.err.println(lines) - } - - /** @inheritdoc - */ - override def shutdown(): Unit = - System.err.flush() -} - -object StderrPrinter { - - /** Creates an instance of [[StderrPrinter]]. - */ - def create(printExceptions: Boolean = false): StderrPrinter = - new StderrPrinter(printExceptions) -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinterWithColors.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinterWithColors.scala deleted file mode 100644 index 5d36427c9071..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/StderrPrinterWithColors.scala +++ /dev/null @@ -1,66 +0,0 @@ -package org.enso.loggingservice.printers -import com.typesafe.scalalogging.Logger -import org.enso.loggingservice.internal.{ - ANSIColorsMessageRenderer, - AnsiTerminal -} -import org.enso.loggingservice.internal.protocol.WSLogMessage - -import scala.io.AnsiColor - -/** Prints the log messages to the standard error output with ANSI escape codes - * that allow to display log levels in color. - * - * @param printExceptions specifies if attached exceptions should be printed - */ -class StderrPrinterWithColors private (printExceptions: Boolean) - extends Printer { - private val renderer = new ANSIColorsMessageRenderer(printExceptions) - - /** @inheritdoc - */ - override def print(message: WSLogMessage): Unit = { - val lines = renderer.render(message) - System.err.println(lines) - } - - /** @inheritdoc - */ - override def shutdown(): Unit = { - System.err.print(AnsiColor.RESET) - System.err.flush() - } -} - -object StderrPrinterWithColors { - - /** Returns a color-supporting printer if color output is available in the - * console. - */ - def colorPrinterIfAvailable(printExceptions: Boolean): Printer = - if (AnsiTerminal.canUseColors()) - new StderrPrinterWithColors(printExceptions) - else new StderrPrinter(printExceptions) - - /** Returns a color-supporting printer regardless of if color output is - * available. - * - * Color output may be used even if it is unavailable in cases where the - * output is piped to another application that will support VT colors. This - * call tries to enable the color output in the local console anyway, in case - * it is used with the local console. - * - * If color support cannot be enabled and the output seems to not be piped, a - * warning is issued that colors are not supported. - */ - def forceCreate(printExceptions: Boolean): StderrPrinterWithColors = { - if (!AnsiTerminal.tryEnabling() && !AnsiTerminal.isLikelyPiped) { - Logger[StderrPrinterWithColors].warn( - "Color output requested on stderr console, but it is unavailable. " + - "Unless the output is handled in a special way, the log messages may " + - "be garbled." - ) - } - new StderrPrinterWithColors(printExceptions) - } -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/TestPrinter.scala b/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/TestPrinter.scala deleted file mode 100644 index 303f9d598f72..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/loggingservice/printers/TestPrinter.scala +++ /dev/null @@ -1,38 +0,0 @@ -package org.enso.loggingservice.printers - -import org.enso.loggingservice.TestLogger.TestLogMessage -import org.enso.loggingservice.internal.protocol.WSLogMessage - -/** A [[Printer]] instance that may be used in tests to gather reported log - * messages. - */ -class TestPrinter extends Printer { - private val messages = - scala.collection.mutable.ListBuffer.empty[TestLogMessage] - - /** @inheritdoc - */ - override def print(message: WSLogMessage): Unit = { - val testMessage = - TestLogMessage(logLevel = message.level, message = message.message) - messages.synchronized { - messages.append(testMessage) - } - } - - @volatile private var alreadyShutdown = false - - /** @inheritdoc - */ - def shutdown(): Unit = { - alreadyShutdown = true - } - - /** Reports if [[shutdown]] has been called. - */ - def wasShutdown: Boolean = alreadyShutdown - - /** Returns all log messages that have been gathered so far. - */ - def getLoggedMessages: Seq[TestLogMessage] = messages.toSeq -} diff --git a/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java b/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java deleted file mode 100644 index dfcba4e44737..000000000000 --- a/lib/scala/logging-service/src/test/java/org/enso/loggingservice/JavaLoggingLogHandlerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.enso.loggingservice; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import org.enso.loggingservice.internal.InternalLogMessage; -import org.enso.loggingservice.internal.LoggerConnection; -import org.junit.Test; -import scala.collection.immutable.Map; - -public class JavaLoggingLogHandlerTest { - - @Test - public void verifyFormatting() { - var c = - new LoggerConnection() { - final List messages = new ArrayList<>(); - - @Override - public void send(InternalLogMessage message) { - messages.add(message); - } - - @Override - public LogLevel logLevel() { - throw new UnsupportedOperationException(); - } - - @Override - public Map loggers() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isEnabled(String name, LogLevel level) { - return true; - } - }; - var h = new JavaLoggingLogHandler((v1) -> LogLevel.Debug$.MODULE$, c); - - LogRecord r = new LogRecord(Level.SEVERE, "Init {} done"); - r.setParameters(new Object[] {"js"}); - - h.publish(r); - - assertEquals("One message: " + c.messages, 1, c.messages.size()); - assertEquals("Init js done", c.messages.get(0).message()); - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/DefaultLogMessageRendererSpec.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/DefaultLogMessageRendererSpec.scala deleted file mode 100644 index 34d067d537db..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/DefaultLogMessageRendererSpec.scala +++ /dev/null @@ -1,59 +0,0 @@ -package org.enso.loggingservice.internal - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.{ - SerializedException, - WSLogMessage -} -import org.scalatest.OptionValues -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -import java.time.Instant -import java.time.temporal.ChronoUnit - -class DefaultLogMessageRendererSpec - extends AnyWordSpec - with Matchers - with OptionValues { - - "DefaultLogMessageRenderer" should { - "render NullPointerException" in { - val renderer = new DefaultLogMessageRenderer(printExceptions = true) - val ts = Instant.now().truncatedTo(ChronoUnit.MILLIS) - - val exception = - SerializedException.fromException(new NullPointerException) - val message = - WSLogMessage(LogLevel.Trace, ts, "group", "message", Some(exception)) - - noException should be thrownBy renderer.render(message) - } - - "render NullPointerException with null message" in { - val renderer = new DefaultLogMessageRenderer(printExceptions = true) - val ts = Instant.now().truncatedTo(ChronoUnit.MILLIS) - - val exception = - SerializedException.fromException(new NullPointerException) - val message = - WSLogMessage(LogLevel.Trace, ts, null, null, Some(exception)) - - val txt = renderer.render(message) - txt.toString() should equal(txt) - } - - "JSONize NullPointerException with null message" in { - val ts = Instant.now().truncatedTo(ChronoUnit.MILLIS) - - val exception = - SerializedException.fromException(new NullPointerException) - val message = - WSLogMessage(LogLevel.Trace, ts, null, null, Some(exception)) - - import io.circe.syntax._ - - message.asJson.noSpaces - } - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/SerializedExceptionSpec.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/SerializedExceptionSpec.scala deleted file mode 100644 index c5773066c34a..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/SerializedExceptionSpec.scala +++ /dev/null @@ -1,48 +0,0 @@ -package org.enso.loggingservice.internal - -import io.circe.syntax._ -import org.enso.loggingservice.internal.protocol.SerializedException -import org.scalatest.OptionValues -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -class SerializedExceptionSpec - extends AnyWordSpec - with Matchers - with OptionValues { - - "SerializedException" should { - "serialize and deserialize with nested causes" in { - val cause = SerializedException( - "cause", - "msg", - Seq(SerializedException.TraceElement("e1", "loc1")) - ) - - val exception = SerializedException( - "root", - "msg2", - Seq( - SerializedException.TraceElement("e2", "loc2"), - SerializedException.TraceElement("e3", "loc3") - ), - cause - ) - - exception.asJson - .as[SerializedException] - .toOption - .value shouldEqual exception - } - - "be created from NullPointerException" in { - val exception = new NullPointerException() - val result = SerializedException.fromException(exception) - - result.name shouldEqual exception.getClass.getName - result.message shouldEqual None - result.cause shouldEqual None - result.stackTrace shouldBe Symbol("nonEmpty") - } - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/WSLogMessageSpec.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/WSLogMessageSpec.scala deleted file mode 100644 index e19c5736b6a4..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/WSLogMessageSpec.scala +++ /dev/null @@ -1,44 +0,0 @@ -package org.enso.loggingservice.internal - -import java.time.Instant -import java.time.temporal.ChronoUnit - -import io.circe.syntax._ -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.internal.protocol.{ - SerializedException, - WSLogMessage -} -import org.scalatest.OptionValues -import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.AnyWordSpec - -class WSLogMessageSpec extends AnyWordSpec with Matchers with OptionValues { - - "WSLogMessage" should { - "serialize and deserialize to the same thing" in { - val ts = Instant.now().truncatedTo(ChronoUnit.MILLIS) - - val message1 = WSLogMessage(LogLevel.Trace, ts, "group", "message", None) - message1.asJson.as[WSLogMessage].toOption.value shouldEqual message1 - noException should be thrownBy message1.asJson.noSpaces - - val exception = SerializedException("name", "message", Seq()) - val message2 = - WSLogMessage(LogLevel.Trace, ts, "group", "message", Some(exception)) - message2.asJson.as[WSLogMessage].toOption.value shouldEqual message2 - noException should be thrownBy message2.asJson.noSpaces - } - - "serialize NullPointerException" in { - val ts = Instant.now().truncatedTo(ChronoUnit.MILLIS) - - val exception = - SerializedException.fromException(new NullPointerException) - val message = - WSLogMessage(LogLevel.Trace, ts, "group", "message", Some(exception)) - message.asJson.as[WSLogMessage].toOption.value shouldEqual message - noException should be thrownBy message.asJson.noSpaces - } - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ClientServerServiceSpec.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ClientServerServiceSpec.scala deleted file mode 100644 index e08451e1b40a..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ClientServerServiceSpec.scala +++ /dev/null @@ -1,81 +0,0 @@ -package org.enso.loggingservice.internal.service - -import java.time.Instant -import java.util.concurrent.{Semaphore, TimeUnit} - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.TestLogger.TestLogMessage -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.internal.{ - BlockingConsumerMessageQueue, - InternalLogMessage -} -import org.enso.loggingservice.printers.TestPrinter - -class ClientServerServiceSpec extends ServiceTest { - "Client and Server" should { - "communicate" in { - val serverQueue = new BlockingConsumerMessageQueue() - val clientQueue = new BlockingConsumerMessageQueue() - val semaphore = new Semaphore(0) - val testPrinter = new TestPrinter { - override def print(message: WSLogMessage): Unit = { - super.print(message) - semaphore.release() - } - } - val message = - InternalLogMessage( - LogLevel.Debug, - Instant.now(), - "group", - "message", - None - ) - val skippedMessage = - InternalLogMessage( - LogLevel.Trace, - Instant.now(), - "group", - "message", - None - ) - - val server = - Server.setup( - "localhost", - 0, - serverQueue, - Seq(testPrinter), - LogLevel.Off - ) - try { - val uri = server.getBinding().toUri() - val client = Client.setup(uri, clientQueue, LogLevel.Debug) - try { - clientQueue.send(Left(message)) - clientQueue.send(Left(skippedMessage)) - assert( - semaphore.tryAcquire(1, 5000, TimeUnit.MILLISECONDS), - "; Waiting for messages timed out." - ) - testPrinter.getLoggedMessages shouldEqual Seq( - TestLogMessage(LogLevel.Debug, "message") - ) - } finally { - client.terminate() - } - } finally { - server.terminate() - } - } - } - - "Server" should { - "also gather local messages" in { - testServiceMessageGathering { (logLevel, queue, printers) => - Server.setup("localhost", 0, queue, printers, logLevel) - } - } - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/LocalServiceSpec.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/LocalServiceSpec.scala deleted file mode 100644 index 803d3decf09d..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/LocalServiceSpec.scala +++ /dev/null @@ -1,11 +0,0 @@ -package org.enso.loggingservice.internal.service - -class LocalServiceSpec extends ServiceTest { - "Local service" should { - "gather messages" in { - testServiceMessageGathering { (logLevel, queue, printers) => - Local.setup(logLevel, queue, printers) - } - } - } -} diff --git a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ServiceTest.scala b/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ServiceTest.scala deleted file mode 100644 index 7b45651627bf..000000000000 --- a/lib/scala/logging-service/src/test/scala/org/enso/loggingservice/internal/service/ServiceTest.scala +++ /dev/null @@ -1,75 +0,0 @@ -package org.enso.loggingservice.internal.service - -import java.time.Instant -import java.util.concurrent.{Semaphore, TimeUnit} - -import org.enso.loggingservice.LogLevel -import org.enso.loggingservice.TestLogger.TestLogMessage -import org.enso.loggingservice.internal.{ - BlockingConsumerMessageQueue, - InternalLogMessage -} -import org.enso.loggingservice.internal.protocol.WSLogMessage -import org.enso.loggingservice.printers.{Printer, TestPrinter} -import org.scalatest.OptionValues -import org.scalatest.concurrent.TimeLimitedTests -import org.scalatest.matchers.should.Matchers -import org.scalatest.time.Span -import org.scalatest.time.SpanSugar.convertIntToGrainOfTime -import org.scalatest.wordspec.AnyWordSpec - -trait ServiceTest - extends AnyWordSpec - with Matchers - with OptionValues - with TimeLimitedTests { - - override def timeLimit: Span = 20.seconds - - def testServiceMessageGathering( - serviceConstructor: ( - LogLevel, - BlockingConsumerMessageQueue, - Seq[Printer] - ) => Service - ): Unit = { - val queue = new BlockingConsumerMessageQueue() - val semaphore = new Semaphore(0) - val testPrinter = new TestPrinter { - override def print(message: WSLogMessage): Unit = { - super.print(message) - semaphore.release() - } - } - val message = - InternalLogMessage( - LogLevel.Debug, - Instant.now(), - "group", - "message", - None - ) - val skippedMessage = - InternalLogMessage( - LogLevel.Trace, - Instant.now(), - "group", - "message", - None - ) - - queue.send(Left(message)) - val service = serviceConstructor(LogLevel.Debug, queue, Seq(testPrinter)) - queue.send(Left(skippedMessage)) - assert( - semaphore.tryAcquire(1, 5000, TimeUnit.MILLISECONDS), - "; Waiting for messages timed out." - ) - service.terminate() - assert(testPrinter.wasShutdown) - - testPrinter.getLoggedMessages shouldEqual Seq( - TestLogMessage(LogLevel.Debug, "message") - ) - } -} From 3faffe3bad72ff65108ea09fd5934b6c6dd9136f Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Wed, 23 Aug 2023 12:16:36 +0200 Subject: [PATCH 15/31] Ensure logging impl is not accidentally used Ensured that imported subprojects don't add logback in transitive dependencies and one has to explicitly enable it. --- build.sbt | 39 ++++++++----------- .../main/java/org/enso/logger/Converter.java | 9 ----- .../enso/logging/LoggingServiceManager.scala | 6 +-- .../java/org/enso/logger/LoggerUtils.java | 12 ++++++ .../runtimeversionmanager/cli/Arguments.scala | 5 +-- 5 files changed, 34 insertions(+), 37 deletions(-) rename lib/scala/{logging-utils => logging-jutil}/src/main/java/org/enso/logger/Converter.java (90%) create mode 100644 lib/scala/logging-utils/src/main/java/org/enso/logger/LoggerUtils.java diff --git a/build.sbt b/build.sbt index 06f45d92b28a..59b5716c6a2e 100644 --- a/build.sbt +++ b/build.sbt @@ -349,6 +349,10 @@ val akkaVersion = "2.6.20" val akkaHTTPVersion = "10.2.10" val akkaMockSchedulerVersion = "0.5.5" val logbackClassicVersion = "1.3.7" +val logbackPkg = Seq( + "ch.qos.logback" % "logback-classic" % logbackClassicVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion, +) val akkaActor = akkaPkg("actor") val akkaStream = akkaPkg("stream") val akkaTyped = akkaPkg("actor-typed") @@ -357,9 +361,7 @@ val akkaSLF4J = akkaPkg("slf4j") val akkaTestkitTyped = akkaPkg("actor-testkit-typed") % Test val akkaHttp = akkaHTTPPkg("http") val akkaSpray = akkaHTTPPkg("http-spray-json") -val akkaTest = Seq( - "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test -) +val akkaTest = logbackPkg.map( _ % Test) val akka = Seq( akkaActor, @@ -683,10 +685,8 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, - "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test - ) + ) ++ logbackPkg.map(_ % Test) ) lazy val `logging-server` = project @@ -697,11 +697,9 @@ lazy val `logging-server` = project version := "0.1", libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, - "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp - ) + ) ++ logbackPkg.map(_ % Provided) ) .dependsOn(`logging-logback`) .dependsOn(`logging-utils`) @@ -737,12 +735,10 @@ lazy val `logging-logback` = project version := "0.1", libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, - "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, "com.typesafe" % "config" % typesafeConfigVersion, "io.sentry" % "sentry-logback" % "6.28.0" % Provided, akkaHttp - ) + ) ++ logbackPkg.map(_ % Provided) ) .dependsOn(`logging-config`) @@ -874,7 +870,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) "com.beachape" %% "enumeratum-circe" % enumeratumCirceVersion, "com.miguno.akka" %% "akka-mock-scheduler" % akkaMockSchedulerVersion % Test, "org.mockito" %% "mockito-scala" % mockitoScalaVersion % Test - ), + ) ++ logbackPkg.map(_ % Runtime), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ) @@ -899,7 +895,6 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .buildNativeImage( "project-manager", staticOnLinux = true, - additionalOptions = Seq("-H:IncludeResources=.*logback.xml$"), initializeAtRuntime = Seq( "scala.util.Random", "zio.internal.ZScheduler$$anon$4", @@ -1004,9 +999,8 @@ lazy val searcher = project libraryDependencies ++= jmh ++ Seq( "com.typesafe.slick" %% "slick" % slickVersion, "org.xerial" % "sqlite-jdbc" % sqliteVersion, - "ch.qos.logback" % "logback-classic" % logbackClassicVersion % Test, "org.scalatest" %% "scalatest" % scalatestVersion % Test - ) + ) ++ logbackPkg.map(_ % Test) ) .configs(Benchmark) .settings( @@ -1465,7 +1459,6 @@ lazy val runtime = (project in file("engine/runtime")) .dependsOn(`text-buffer`) .dependsOn(`runtime-parser`) .dependsOn(pkg) - .dependsOn(`edition-updater`) .dependsOn(`connected-lock-manager`) .dependsOn(testkit % Test) @@ -1691,13 +1684,13 @@ lazy val `engine-runner` = project commands += WithDebugCommand.withDebug, inConfig(Compile)(truffleRunOptionsSettings), libraryDependencies ++= Seq( - "org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided", - "org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided", + "org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % Provided, + "org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Provided, "commons-cli" % "commons-cli" % commonsCliVersion, "com.monovore" %% "decline" % declineVersion, "org.jline" % "jline" % jlineVersion, "org.typelevel" %% "cats-core" % catsVersion - ), + ) ++ logbackPkg.map(_ % Runtime), run / connectInput := true ) .settings( @@ -1748,8 +1741,10 @@ lazy val `engine-runner` = project .dependsOn(cli) .dependsOn(`library-manager`) .dependsOn(`language-server`) + .dependsOn(`logging-jutil`) + .dependsOn(`edition-updater`) + .dependsOn(`logging-server`) .dependsOn(`polyglot-api`) - .dependsOn(`logging-logback`) lazy val launcher = project .in(file("engine/launcher")) @@ -1763,7 +1758,7 @@ lazy val launcher = project "org.apache.commons" % "commons-compress" % commonsCompressVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaSLF4J - ) + ) ++ logbackPkg.map(_ % Runtime) ) .settings( rebuildNativeImage := NativeImage diff --git a/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java similarity index 90% rename from lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java rename to lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java index f9fc31f36024..24e003894d2d 100644 --- a/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java @@ -12,15 +12,6 @@ public class Converter { private static int defaultLevelDebugCutOff = Math.min(java.util.logging.Level.FINE.intValue(), java.util.logging.Level.CONFIG.intValue()); - public static String backwardCompatibleName(String name) { - switch (name) { - case "warning": - return "warn"; - default: - return name; - } - } - /** * Converts SLF4J's Level to java.util one. * diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala index 34cd63162d61..014e82107f7e 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -27,10 +27,10 @@ object LoggingServiceManager { throw new LoggingServiceAlreadySetup() } else { currentLevel = logLevel - val forwarder = new LoggingServer(port) - loggingService = forwarder + val server = new LoggingServer(port) + loggingService = server Future { - forwarder.start(logLevel, logPath, logFileSuffix, appender) + server.start(logLevel, logPath, logFileSuffix, appender) } } } diff --git a/lib/scala/logging-utils/src/main/java/org/enso/logger/LoggerUtils.java b/lib/scala/logging-utils/src/main/java/org/enso/logger/LoggerUtils.java new file mode 100644 index 000000000000..5a245846b08d --- /dev/null +++ b/lib/scala/logging-utils/src/main/java/org/enso/logger/LoggerUtils.java @@ -0,0 +1,12 @@ +package org.enso.logger; + +public class LoggerUtils { + public static String backwardCompatibleName(String name) { + switch (name) { + case "warning": + return "warn"; + default: + return name; + } + } +} diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala index ee2119956368..18038858705b 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala @@ -2,11 +2,10 @@ package org.enso.runtimeversionmanager.cli import akka.http.scaladsl.model.{IllegalUriException, Uri} import org.enso.cli.arguments.{Argument, OptsParseError} -import org.enso.logger.Converter +import org.enso.logger.LoggerUtils import java.net.URI import java.net.URISyntaxException -//import org.enso.loggingservice.LogLevel import org.slf4j.event.Level object Arguments { @@ -29,7 +28,7 @@ object Arguments { } implicit val logLevelArgument: Argument[Level] = (string: String) => { - val provided = Converter.backwardCompatibleName(string.toLowerCase) + val provided = LoggerUtils.backwardCompatibleName(string.toLowerCase) Level .values() //LogLevel.allLevels From d3d3e0464ec0d05680f9538a204be7396ae2787e Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 24 Aug 2023 12:01:32 +0200 Subject: [PATCH 16/31] Separate logger implementation from setup api Logback implementation is now completely separate from the API used to setup logging in different services. Loading is done once, by reading implementation classes from the configuration file and intializing via reflection. Also discovered and fixed various problems with native image compilation. More documentation that hopefully helps to clarify the usage. --- build.sbt | 85 ++++++++++--------- .../src/main/resources/application.conf | 13 +-- .../handler/LibraryPublishHandler.scala | 7 +- .../org/enso/launcher/reflect-config.json | 17 ++++ .../org/enso/launcher/resource-config.json | 1 + .../src/main/resources/application.conf | 6 +- .../scala/org/enso/launcher/Launcher.scala | 1 - .../enso/launcher/cli/LauncherLogging.scala | 4 +- .../scala/org/enso/launcher/cli/Main.scala | 2 +- .../org/enso/runner/reflect-config.json | 52 ++++++++++++ .../org/enso/runner/resource-config.json | 9 ++ .../scala/org/enso/runner/RunnerLogging.scala | 7 +- .../org/enso/cli/arguments/Application.scala | 1 + .../java/org/enso/logger/config/Appender.java | 46 +++++++--- .../org/enso/logger/config/LoggerSetup.java | 79 +++++++++++++++-- .../logger/config/LoggerSetupFactory.java | 34 ++++++++ .../org/enso/logger/config/LoggingServer.java | 12 ++- .../logger/config/LoggingServiceConfig.java | 4 + .../enso/logger/config/SocketAppender.java | 6 -- .../org/enso/logger/ApplicationFilter.java | 4 + .../java/org/enso/logger/LogbackSetup.java | 14 +-- .../logging/LogbackLoggingServiceFactory.java | 11 +++ .../java/org/enso/logging/LoggingServer.java | 12 +-- .../src/main/resources/enso-logging.conf | 2 + .../java/org/enso/logging/LoggingService.java | 5 -- .../logging/LoggerInitializationFailed.java | 0 .../java/org/enso/logging/LoggingService.java | 28 ++++++ .../logging/LoggingServiceAlreadySetup.java | 0 .../enso/logging/LoggingServiceFactory.java | 38 +++++++++ .../enso/logging/LoggingServiceManager.scala | 6 +- .../org/enso/logging/LoggingSetupHelper.scala | 33 ++++--- .../enso/projectmanager/reflect-config.json | 45 ++++++++++ .../enso/projectmanager/resource-config.json | 2 + .../src/main/resources/application.conf | 15 ++-- .../ExecutorWithUnlimitedPool.scala | 1 - 35 files changed, 465 insertions(+), 137 deletions(-) create mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java create mode 100644 lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java rename lib/scala/{logging-server => logging-logback}/src/main/java/org/enso/logging/LoggingServer.java (80%) create mode 100644 lib/scala/logging-logback/src/main/resources/enso-logging.conf delete mode 100644 lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java rename lib/scala/{logging-server => logging-service}/src/main/java/org/enso/logging/LoggerInitializationFailed.java (100%) create mode 100644 lib/scala/logging-service/src/main/java/org/enso/logging/LoggingService.java rename lib/scala/{logging-server => logging-service}/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java (100%) create mode 100644 lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java rename lib/scala/{logging-server => logging-service}/src/main/scala/org/enso/logging/LoggingServiceManager.scala (80%) rename lib/scala/{logging-server => logging-service}/src/main/scala/org/enso/logging/LoggingSetupHelper.scala (82%) diff --git a/build.sbt b/build.sbt index 59b5716c6a2e..89ce763ec5d3 100644 --- a/build.sbt +++ b/build.sbt @@ -269,7 +269,7 @@ lazy val enso = (project in file(".")) `logging-jutil`, `logging-config`, `logging-logback`, - `logging-server`, + `logging-service`, `logging-utils-akka`, filewatcher, `logging-truffle-connector`, @@ -342,26 +342,26 @@ lazy val enso = (project in file(".")) // === Akka =================================================================== -def akkaPkg(name: String) = akkaURL %% s"akka-$name" % akkaVersion -def akkaHTTPPkg(name: String) = akkaURL %% s"akka-$name" % akkaHTTPVersion +def akkaPkg(name: String) = akkaURL %% s"akka-$name" % akkaVersion +def akkaHTTPPkg(name: String) = akkaURL %% s"akka-$name" % akkaHTTPVersion val akkaURL = "com.typesafe.akka" val akkaVersion = "2.6.20" val akkaHTTPVersion = "10.2.10" val akkaMockSchedulerVersion = "0.5.5" val logbackClassicVersion = "1.3.7" -val logbackPkg = Seq( - "ch.qos.logback" % "logback-classic" % logbackClassicVersion, - "ch.qos.logback" % "logback-core" % logbackClassicVersion, +val logbackPkg = Seq( + "ch.qos.logback" % "logback-classic" % logbackClassicVersion, + "ch.qos.logback" % "logback-core" % logbackClassicVersion ) -val akkaActor = akkaPkg("actor") -val akkaStream = akkaPkg("stream") -val akkaTyped = akkaPkg("actor-typed") -val akkaTestkit = akkaPkg("testkit") -val akkaSLF4J = akkaPkg("slf4j") -val akkaTestkitTyped = akkaPkg("actor-testkit-typed") % Test -val akkaHttp = akkaHTTPPkg("http") -val akkaSpray = akkaHTTPPkg("http-spray-json") -val akkaTest = logbackPkg.map( _ % Test) +val akkaActor = akkaPkg("actor") +val akkaStream = akkaPkg("stream") +val akkaTyped = akkaPkg("actor-typed") +val akkaTestkit = akkaPkg("testkit") +val akkaSLF4J = akkaPkg("slf4j") +val akkaTestkitTyped = akkaPkg("actor-testkit-typed") % Test +val akkaHttp = akkaHTTPPkg("http") +val akkaSpray = akkaHTTPPkg("http-spray-json") +val akkaTest = logbackPkg.map(_ % Test) val akka = Seq( akkaActor, @@ -685,24 +685,25 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ++ logbackPkg.map(_ % Test) ) -lazy val `logging-server` = project - .in(file("lib/scala/logging-server")) +lazy val `logging-service` = project + .in(file("lib/scala/logging-service")) .configs(Test) .settings( frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "org.slf4j" % "slf4j-api" % slf4jVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaHttp - ) ++ logbackPkg.map(_ % Provided) + ) ) - .dependsOn(`logging-logback`) .dependsOn(`logging-utils`) + .dependsOn(`logging-config`) lazy val `logging-jutil` = project .in(file("lib/scala/logging-jutil")) @@ -734,13 +735,14 @@ lazy val `logging-logback` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "io.sentry" % "sentry-logback" % "6.28.0" % Provided, + "org.slf4j" % "slf4j-api" % slf4jVersion, + "com.typesafe" % "config" % typesafeConfigVersion, + "io.sentry" % "sentry-logback" % "6.28.0" % Provided, akkaHttp - ) ++ logbackPkg.map(_ % Provided) + ) ++ logbackPkg ) .dependsOn(`logging-config`) + .dependsOn(`logging-service`) lazy val `logging-utils-akka` = project .in(file("lib/scala/logging-utils-akka")) @@ -764,7 +766,7 @@ lazy val filewatcher = project "io.methvin" % "directory-watcher" % directoryWatcherVersion, "commons-io" % "commons-io" % commonsIoVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test - ) + ) ++ logbackPkg.map(_ % Test) ) .dependsOn(testkit % Test) @@ -870,7 +872,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) "com.beachape" %% "enumeratum-circe" % enumeratumCirceVersion, "com.miguno.akka" %% "akka-mock-scheduler" % akkaMockSchedulerVersion % Test, "org.mockito" %% "mockito-scala" % mockitoScalaVersion % Test - ) ++ logbackPkg.map(_ % Runtime), + ), addCompilerPlugin( "org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full ) @@ -894,7 +896,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) rebuildNativeImage := NativeImage .buildNativeImage( "project-manager", - staticOnLinux = true, + staticOnLinux = true, initializeAtRuntime = Seq( "scala.util.Random", "zio.internal.ZScheduler$$anon$4", @@ -923,9 +925,10 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .dependsOn(`runtime-version-manager`) .dependsOn(`library-manager`) .dependsOn(`logging-utils-akka`) - .dependsOn(`logging-server`) + .dependsOn(`logging-service`) .dependsOn(pkg) .dependsOn(`json-rpc-server`) + .dependsOn(`logging-logback` % Runtime) .dependsOn(`json-rpc-server-test` % Test) .dependsOn(testkit % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -997,9 +1000,9 @@ lazy val searcher = project .settings( frgaalJavaCompilerSetting, libraryDependencies ++= jmh ++ Seq( - "com.typesafe.slick" %% "slick" % slickVersion, - "org.xerial" % "sqlite-jdbc" % sqliteVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test + "com.typesafe.slick" %% "slick" % slickVersion, + "org.xerial" % "sqlite-jdbc" % sqliteVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ++ logbackPkg.map(_ % Test) ) .configs(Benchmark) @@ -1161,7 +1164,7 @@ lazy val `language-server` = (project in file("engine/language-server")) Test / javaOptions ++= { // Note [Classpath Separation] val runtimeClasspath = - (LocalProject("runtime") / Compile / fullClasspath).value + (LocalProject("runtime") / Runtime / fullClasspath).value .map(_.data) .mkString(File.pathSeparator) Seq( @@ -1183,7 +1186,7 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`connected-lock-manager`) .dependsOn(`edition-updater`) .dependsOn(`logging-utils-akka`) - .dependsOn(`logging-server`) + .dependsOn(`logging-service`) .dependsOn(`logging-jutil`) .dependsOn(`polyglot-api`) .dependsOn(`searcher`) @@ -1193,6 +1196,7 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`profiling-utils`) .dependsOn(filewatcher) .dependsOn(testkit % Test) + .dependsOn(`logging-logback` % Test) .dependsOn(`library-manager-test` % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -1690,7 +1694,7 @@ lazy val `engine-runner` = project "com.monovore" %% "decline" % declineVersion, "org.jline" % "jline" % jlineVersion, "org.typelevel" %% "cats-core" % catsVersion - ) ++ logbackPkg.map(_ % Runtime), + ), run / connectInput := true ) .settings( @@ -1743,7 +1747,8 @@ lazy val `engine-runner` = project .dependsOn(`language-server`) .dependsOn(`logging-jutil`) .dependsOn(`edition-updater`) - .dependsOn(`logging-server`) + .dependsOn(`logging-service`) + .dependsOn(`logging-logback` % Runtime) .dependsOn(`polyglot-api`) lazy val launcher = project @@ -1758,7 +1763,7 @@ lazy val launcher = project "org.apache.commons" % "commons-compress" % commonsCompressVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test, akkaSLF4J - ) ++ logbackPkg.map(_ % Runtime) + ) ) .settings( rebuildNativeImage := NativeImage @@ -1810,7 +1815,9 @@ lazy val launcher = project .dependsOn(`version-output`) .dependsOn(pkg) .dependsOn(`logging-utils` % "test->test") - .dependsOn(`logging-server`) + .dependsOn(`logging-service`) + .dependsOn(`logging-logback` % Test) + .dependsOn(`logging-logback` % Runtime) .dependsOn(`distribution-manager` % Test) .dependsOn(`runtime-version-manager-test` % Test) diff --git a/engine/language-server/src/main/resources/application.conf b/engine/language-server/src/main/resources/application.conf index f06aed348b98..73876699e58a 100644 --- a/engine/language-server/src/main/resources/application.conf +++ b/engine/language-server/src/main/resources/application.conf @@ -24,22 +24,23 @@ logging-service { slick."*" = error org.eclipse.jgit = error io.methvin.watcher = error - org.enso.languageserver.protocol.json.JsonConnectionController = debug # very verbose in trace mode - org.enso.jsonrpc.JsonRpcServer = debug # verbose in trace mode - org.enso.languageserver.runtime.RuntimeConnector = debug + # Log levels to limit during very verbose setting: + #org.enso.languageserver.protocol.json.JsonConnectionController = debug + #org.enso.jsonrpc.JsonRpcServer = debug + #org.enso.languageserver.runtime.RuntimeConnector = debug } appenders = [ { name = "socket" - hostname = ${?LOGSERVER_HOSTNAME} hostname = "localhost" - port = ${?LOGSERVER_PORT} + hostname = ${?ENSO_LOGSERVER_HOSTNAME} port = 6000 + port = ${?ENSO_LOGSERVER_PORT} }, { name = "console" } ] - default-appender = ${?DEFAULT_LOGGER} default-appender = socket + default-appender = ${?ENSO_DEFAULT_APPENDER} } diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala index 04c6bae4ee5e..cb715bdc1947 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/handler/LibraryPublishHandler.scala @@ -19,9 +19,7 @@ import org.enso.languageserver.libraries.{ import org.enso.languageserver.requesthandler.RequestTimeout import org.enso.languageserver.util.UnhandledLogging import org.enso.libraryupload.{auth, LibraryUploader} -import org.enso.logger.Converter -//import org.slf4j.LoggerFactory -//import org.enso.loggingservice.LoggingServiceManager +import org.enso.logging.LoggingServiceManager import scala.concurrent.Future import scala.concurrent.duration.FiniteDuration @@ -104,8 +102,7 @@ class LibraryPublishHandler( ) val future: Future[UploadSucceeded] = BlockingOperation.run { - val logLevel = - Converter.defaultLogLevel //LoggingServiceManager.currentLogLevelForThisApplication() + val logLevel = LoggingServiceManager.currentLogLevelForThisApplication() val dependencyExtractor = new CompilerBasedDependencyExtractor(logLevel) LibraryUploader(dependencyExtractor) diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json index 353bee804b36..af842993928a 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json @@ -722,5 +722,22 @@ "parameterTypes": ["java.lang.Boolean", "java.lang.Object"] } ] + }, + { + "name": "org.enso.logging.LogbackLoggingServiceFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "org.enso.logger.LogbackSetup", + "methods": [ + { + "name": "get" + } + ] } ] diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json index 044086ab6f65..df33089fd238 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json @@ -3,6 +3,7 @@ { "pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, { "pattern": "\\Qakka-http-version.conf\\E" }, { "pattern": "\\Qapplication.conf\\E" }, + { "pattern": "\\Qenso-logging.conf\\E" }, { "pattern": "\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, { "pattern": "\\Qreference.conf\\E" }, { "pattern": "\\Qversion.conf\\E" } diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index 2c265c7882e7..d569e2d9e322 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -16,10 +16,10 @@ logging-service { appenders = [ { name = "socket" - hostname = ${?LOGSERVER_HOSTNAME} hostname = "localhost" - port = ${?LOGSERVER_PORT} + hostname = ${?ENSO_LOGSERVER_PORT} port = 6000 + port = ${?ENSO_LOGSERVER_PORT} }, { name = "file", @@ -30,6 +30,6 @@ logging-service { pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] - default-appender = ${?DEFAULT_LOGGER} default-appender = file + default-appender = ${?ENSO_DEFAULT_APPENDER} } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala index 27195fab8f35..bdca701c7c87 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/Launcher.scala @@ -25,7 +25,6 @@ import org.enso.launcher.installation.{ import org.enso.launcher.project.ProjectManager import org.enso.launcher.upgrade.LauncherUpgrader import org.slf4j.event.Level -//import org.enso.loggingservice.LogLevel import org.enso.version.{VersionDescription, VersionDescriptionParameter} /** Implements launcher commands that are run from CLI and can be affected by diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 351e5adb1940..0aa9bd79c2ee 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -2,7 +2,7 @@ package org.enso.launcher.cli import java.nio.file.Path import org.enso.launcher.distribution.DefaultManagers -import org.enso.logger.LogbackSetup +import org.enso.logger.config.LoggerSetupFactory import org.slf4j.event.Level import org.enso.logging.LoggingSetupHelper import scala.concurrent.ExecutionContext.Implicits.global @@ -33,6 +33,6 @@ object LauncherLogging extends LoggingSetupHelper { def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LogbackSetup.get().setupConsoleAppender(actualLogLevel) + LoggerSetupFactory.get().setupConsoleAppender(actualLogLevel) } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index bfd29e46d29d..386a81d8d27c 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -15,7 +15,7 @@ object Main { private def runAppHandlingParseErrors(args: Array[String]): Int = LauncherApplication.application.run(args) match { case Left(errors) => - LauncherLogging.prepareForUninstall(None) + LauncherLogging.setupFallback() CLIOutput.println(errors.mkString("\n")) 1 case Right(exitCode) => diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index 976d3e93f217..0518da90f17e 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -219,5 +219,57 @@ { "name": "org.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField", "methods": [{ "name": "", "parameterTypes": [] }] + }, + { + "name": "org.enso.logging.LogbackLoggingServiceFactory", + "methods": [{ "name": "", "parameterTypes": [] } ] + }, + { "name": "org.enso.logger.LogbackSetup", + "methods": [{ "name": "get" }] + }, + { + "name": "org.enso.logging.LogbackLoggingServiceFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "org.enso.logger.LogbackSetup", + "methods": [ + { + "name": "get" + } + ] + }, + { + "name":"ch.qos.logback.classic.pattern.DateConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LevelConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LoggerConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.MessageConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.IntegerTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json index ff677c20809b..a7b2190f3622 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json @@ -27,6 +27,15 @@ }, { "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" + }, + { + "pattern":"\\Qenso-logging.conf\\E" + }, + { + "pattern":"\\Qapplication.conf\\E" + }, + { + "pattern":"\\Qch/qos/logback/classic/spi/Configurator.class\\E" } ]}, "bundles":[] diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index 1297b4d4c046..e81e97429eb1 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -2,9 +2,8 @@ package org.enso.runner import java.net.URI import com.typesafe.scalalogging.Logger -import org.enso.logger.LogbackSetup +import org.enso.logger.config.LoggerSetupFactory import org.enso.logger.masking.Masking - import org.slf4j.event.Level import scala.util.{Failure, Success} @@ -33,7 +32,7 @@ object RunnerLogging { ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) - val loggerSetup = LogbackSetup.get() + val loggerSetup = LoggerSetupFactory.get() val initializedLogger = connectionUri match { case Some(uri) => Future { @@ -74,6 +73,6 @@ object RunnerLogging { /** Shuts down the logging service gracefully. */ def tearDown(): Unit = { - LogbackSetup.teardown() + LoggerSetupFactory.get().teardown() } } diff --git a/lib/scala/cli/src/main/scala/org/enso/cli/arguments/Application.scala b/lib/scala/cli/src/main/scala/org/enso/cli/arguments/Application.scala index 537001d7e607..90674d9bc230 100644 --- a/lib/scala/cli/src/main/scala/org/enso/cli/arguments/Application.scala +++ b/lib/scala/cli/src/main/scala/org/enso/cli/arguments/Application.scala @@ -83,6 +83,7 @@ class Application[Config]( additionalArguments, applicationName = commandName ) + val finalResult = parseResult.flatMap { case ((topLevelAction, commandResult), pluginIntercepted) => pluginIntercepted match { diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java index 14ac650f4f8d..9299401d9786 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -1,15 +1,29 @@ package org.enso.logger.config; import com.typesafe.config.Config; -import java.net.URI; import java.nio.file.Path; import org.slf4j.event.Level; -/** Base class for all appenders supported in Enso's config file. */ +/** + * Base class for all appenders supported by Enso's logging configuration. Appenders determine what + * to do with the recorded log events + */ public abstract class Appender { + /** + * Returns the name of the appender + * + * @return + */ public abstract String getName(); + /** + * Parses config section and returns an appender's configuration. + * + * @param config section of the config to parse + * @return parsed and verified appender configuration + * @throws MissingConfigurationField if the config file was mis-configured + */ public static Appender parse(Config config) throws MissingConfigurationField { if (config != null) { switch (config.getString(nameKey)) { @@ -28,21 +42,31 @@ public static Appender parse(Config config) throws MissingConfigurationField { return null; } - public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + /** + * Uses this appender's configuration to setup the logger. + * + * @param logLevel maximal level of logs that will be handled by logger + * @param loggerSetup logger's setup to be used to be invoked with this appender + * @return true if logger has been setup correctly using this configuration, false otherwise + */ + public Boolean setup(Level logLevel, LoggerSetup loggerSetup) { return false; } + /** + * Uses this appender's configuration to setup the logger. + * + * @param logLevel maximal level of logs that will be handled by logger + * @param loggerSetup logger's setup to be used to be invoked with this appender + * @return true if logger has been setup correctly using this configuration, false otherwise + */ public Boolean setupForPath( - Level logLevel, Path logRoot, String logPrefix, LoggerSetup appenderSetup) { - return setup(logLevel, appenderSetup); + Level logLevel, Path logRoot, String logPrefix, LoggerSetup loggerSetup) { + return setup(logLevel, loggerSetup); } - public Boolean setupForURI(Level logLevel, String hostname, int port, LoggerSetup appenderSetup) { - return setup(logLevel, appenderSetup); - } - - public boolean isSameTargetAs(URI uri) { - return false; + public Boolean setupForURI(Level logLevel, String hostname, int port, LoggerSetup loggerSetup) { + return setup(logLevel, loggerSetup); } public static final String defaultPattern = diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java index f4a892fc46db..a37d774bc72e 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java @@ -3,29 +3,90 @@ import java.nio.file.Path; import org.slf4j.event.Level; -/** Base classes to be implemented by the underlying loggging implementation. */ +/** Base class to be implemented by the underlying logging implementation. */ public abstract class LoggerSetup { - /** Returns the parsed config used to create this instance * */ + /** Returns parsed application config used to create this instance * */ public abstract LoggingServiceConfig getConfig(); + /** + * Setup forwarding of logger's log event to a logging server. + * + * @param logLevel the maximal level of logs that will be forwarded + * @param hostname the name of the host where server is located + * @param port the port number where server is listening for messages + * @return true if logger was setup correctly, false otherwise + */ public abstract boolean setupSocketAppender(Level logLevel, String hostname, int port); + /** + * Setup writing logger's log event to a file. + * + * @param logLevel the maximal level of logs that will be written + * @param logRoot the root directory where logs are located + * @param logPrefix the prefix used in the name of the log file + * @return true if logger was setup correctly, false otherwise + */ public abstract boolean setupFileAppender(Level logLevel, Path logRoot, String logPrefix); + /** + * Setup writing logger's log event to a plain console. + * + * @param logLevel the maximal level of logs that will be displayed + * @return true if logger was setup correctly, false otherwise + */ public abstract boolean setupConsoleAppender(Level logLevel); + /** + * Setup forwarding logger's log event to a sentry,io service. Requires the presence of the + * sentry's dependency appropriate to the logging implementation. + * + * @param logLevel the maximal level of logs that will be displayed + * @return true if logger was setup correctly, false otherwise + */ public abstract boolean setupSentryAppender(Level logLevel); + /** + * Sets up loggers so that all events are being discarded. + * + * @return true unconditionally + */ public abstract boolean setupNoOpAppender(); - public abstract Boolean setup() throws MissingConfigurationField; + /** + * Sets up logging according to the application's config file. + * + * @return true if logger was setup correctly, false otherwise + * @throws MissingConfigurationField if application's config has been mis-configured + */ + public abstract boolean setup() throws MissingConfigurationField; - public abstract Boolean setup(Level logLevel) throws MissingConfigurationField; + /** + * Sets up logging according to the application's config file while taking into account the + * provided log level. + * + * @param logLevel maximal log level allowed for log events + * @return true if logger was setup correctly, false otherwise + * @throws MissingConfigurationField if application's config has been mis-configured + */ + public abstract boolean setup(Level logLevel) throws MissingConfigurationField; - public abstract Boolean setup( - Level logLevel, - Path componentLogPath, - String componentLogPrefix, - LoggingServiceConfig config); + /** + * Sets up logging according to the provided application's config file and log level. If the + * default logging writes to a file, provided parameters will specify the exact location of the + * log file. This is more specific than {@link #setup(Level)} method. + * + * @param logLevel maximal log level allowed for log events + * @param logRoot the root directory where logs are located + * @param logPrefix the prefix used in the name of the log file + * @oaram config config file to be used to setup loggers (overriding the one returned by {@link + * #getConfig()} + * @return true if logger was setup correctly, false otherwise + * @throws MissingConfigurationField if application's config has been mis-configured + */ + public abstract boolean setup( + Level logLevel, Path logRoot, String logPrefix, LoggingServiceConfig config); + + /** Shuts down all loggers. */ + public abstract void teardown(); } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java new file mode 100644 index 000000000000..e3ba17e0f590 --- /dev/null +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java @@ -0,0 +1,34 @@ +package org.enso.logger.config; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import java.lang.reflect.Method; + +public class LoggerSetupFactory { + + private static LoggerSetup _loaded = null; + + public static LoggerSetup get() { + if (_loaded == null) { + Config c = ConfigFactory.load("enso-logging.conf"); + if (c.hasPath(implClassKey)) { + try { + String clazzName = c.getString(implClassKey); + Class clazz = Class.forName(clazzName); + Method meth = clazz.getDeclaredMethod("get"); + _loaded = (LoggerSetup) meth.invoke(null); + } catch (Throwable e) { + e.printStackTrace(); + System.err.println("Failed to initialize LoggerSetup configuration class"); + return null; + } + } else { + System.err.println("Missing log configuration class key:" + implClassKey); + return null; + } + } + return _loaded; + } + + private static final String implClassKey = LoggerSetup.class.getName() + ".impl.class"; +} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java index 8c4bd4c66250..c729bf393218 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java @@ -2,14 +2,20 @@ import com.typesafe.config.Config; -public record LoggingServer(String hostname, int port, Appender appender, Boolean start) { +/** + * Configuration for the local server that collects logs from different services. + * + * @param port port of the local server that accepts logs + * @param appender appender's configuration describing how to transform received log events + * @param start if true, will be started by the service defining the configuration + */ +public record LoggingServer(int port, Appender appender, Boolean start) { public static LoggingServer parse(Config config) throws MissingConfigurationField { int port = config.getInt("port"); - String hostname = config.getString("hostname"); Appender appender = Appender.parse(config.getConfig("appender")); boolean start = config.getBoolean("start"); - return new LoggingServer(hostname, port, appender, start); + return new LoggingServer(port, appender, start); } } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java index cc0661662814..263b4aee398b 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServiceConfig.java @@ -8,6 +8,10 @@ import java.util.Map; import java.util.Optional; +/** + * Parsed and verified representation of `logging-service` section of `application.conf`. Defines + * custom log levels, logging appenders and, optionally, logging server configuration. + */ public class LoggingServiceConfig { public static final String configurationRoot = "logging-service"; public static final String serverKey = "server"; diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java index 7f202f6ff9ba..fe0f6f9a7551 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -1,7 +1,6 @@ package org.enso.logger.config; import com.typesafe.config.Config; -import java.net.URI; import org.slf4j.event.Level; /** Config for log configuration that forwards logs to the network socket as-is. */ @@ -62,11 +61,6 @@ public Boolean setupForURI(Level logLevel, String host, int port, LoggerSetup lo return loggerSetup.setupSocketAppender(logLevel, host, port); } - @Override - public boolean isSameTargetAs(URI uri) { - return uri.getHost().equals(host) && uri.getPort() == port; - } - private static final String hostKey = "hostname"; private static final String portKey = "port"; private static final String reconnectionDelayKey = "reconnection-delay"; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java index fe61f5eaf248..59a105ab1944 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java @@ -6,6 +6,10 @@ import ch.qos.logback.core.spi.FilterReply; import org.enso.logger.config.LoggersLevels; +/** + * An implementation of ch.qos.logback.core.filter.Filter that is created from configuration's and + * user's custom logger levels. + */ public class ApplicationFilter extends Filter { private final LoggersLevels loggers; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java index c6ad6b7fdb58..e8e4714b9d6e 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -67,22 +67,22 @@ public LoggingServiceConfig getConfig() { private final LoggerContext context; @Override - public Boolean setup() throws MissingConfigurationField { + public boolean setup() throws MissingConfigurationField { LoggingServiceConfig config = LoggingServiceConfig.parseConfig(); return setup(config); } - private Boolean setup(LoggingServiceConfig config) { + private boolean setup(LoggingServiceConfig config) { Level defaultLogLevel = config.getLogLevel().map(name -> Level.valueOf(name.toUpperCase())).orElseGet(() -> Level.ERROR); return setup(defaultLogLevel, config); } @Override - public Boolean setup(Level logLevel) throws MissingConfigurationField { + public boolean setup(Level logLevel) throws MissingConfigurationField { return setup(logLevel, LoggingServiceConfig.parseConfig()); } - public Boolean setup(Level logLevel, LoggingServiceConfig config) { + public boolean setup(Level logLevel, LoggingServiceConfig config) { Appender defaultAppender = config.getAppender(); if (defaultAppender != null) { return defaultAppender.setup(logLevel, this); @@ -92,7 +92,7 @@ public Boolean setup(Level logLevel, LoggingServiceConfig config) { } @Override - public Boolean setup(Level logLevel, Path componentLogPath, String componentLogPrefix, LoggingServiceConfig config) { + public boolean setup(Level logLevel, Path componentLogPath, String componentLogPrefix, LoggingServiceConfig config) { Appender defaultAppender = config.getAppender(); if (defaultAppender != null) { return defaultAppender.setupForPath(logLevel, componentLogPath, componentLogPrefix, this); @@ -247,9 +247,9 @@ public boolean setupNoOpAppender() { return true; } - public static void teardown() { + @Override + public void teardown() { // TODO: disable whatever appender is now in place and replace it with console - var context = (LoggerContext) LoggerFactory.getILoggerFactory(); context.stop(); } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java b/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java new file mode 100644 index 000000000000..b99c3adf988f --- /dev/null +++ b/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java @@ -0,0 +1,11 @@ +package org.enso.logging; + +import java.net.URI; + +public class LogbackLoggingServiceFactory extends LoggingServiceFactory { + + @Override + public LoggingService localServerFor(int port) { + return new LoggingServer(port); + } +} diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java b/lib/scala/logging-logback/src/main/java/org/enso/logging/LoggingServer.java similarity index 80% rename from lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java rename to lib/scala/logging-logback/src/main/java/org/enso/logging/LoggingServer.java index 4940fd9e7a9c..54aef2fd1bdd 100644 --- a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServer.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logging/LoggingServer.java @@ -2,7 +2,6 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.net.SimpleSocketServer; -import ch.qos.logback.core.joran.spi.JoranException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; @@ -10,7 +9,7 @@ import org.enso.logger.config.Appender; import org.slf4j.event.Level; -class LoggingServer extends LoggingService { +class LoggingServer extends LoggingService { private int port; private SimpleSocketServer logServer; @@ -20,14 +19,17 @@ public LoggingServer(int port) { this.logServer = null; } - public URI start(Level level, Path path, String prefix, Appender appender) - throws URISyntaxException, JoranException { + public URI start(Level level, Path path, String prefix, Appender appender) { var lc = new LoggerContext(); var setup = LogbackSetup.forContext(lc, appender); setup.setup(level, path, prefix, setup.getConfig()); logServer = new SimpleSocketServer(lc, port); logServer.start(); - return new URI(null, null, "localhost", port, null, null, null); + try { + return new URI(null, null, "localhost", port, null, null, null); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } } public boolean isSetup() { diff --git a/lib/scala/logging-logback/src/main/resources/enso-logging.conf b/lib/scala/logging-logback/src/main/resources/enso-logging.conf new file mode 100644 index 000000000000..b03bfeb1d6c3 --- /dev/null +++ b/lib/scala/logging-logback/src/main/resources/enso-logging.conf @@ -0,0 +1,2 @@ +org.enso.logging.LoggingServiceFactory.impl.class=org.enso.logging.LogbackLoggingServiceFactory +org.enso.logger.config.LoggerSetup.impl.class=org.enso.logger.LogbackSetup diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java b/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java deleted file mode 100644 index b9341782e9b7..000000000000 --- a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingService.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.enso.logging; - -public abstract class LoggingService { - public abstract void teardown(); -} diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggerInitializationFailed.java similarity index 100% rename from lib/scala/logging-server/src/main/java/org/enso/logging/LoggerInitializationFailed.java rename to lib/scala/logging-service/src/main/java/org/enso/logging/LoggerInitializationFailed.java diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingService.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingService.java new file mode 100644 index 000000000000..3c7aca74f1e2 --- /dev/null +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingService.java @@ -0,0 +1,28 @@ +package org.enso.logging; + +import java.nio.file.Path; +import org.enso.logger.config.Appender; +import org.slf4j.event.Level; + +/** + * Base class for any logging service that accepts logs from other Enso's services + * + * @param the type of the object describing on how to communicate with the service + */ +public abstract class LoggingService { + + /** + * Starts the service. The `appender` configuration specifies what to do with the received log + * events. + * + * @param level the maximal log level handled by this service + * @param path + * @param prefix + * @param appender + * @return + */ + public abstract T start(Level level, Path path, String prefix, Appender appender); + + /** Shuts down the service. */ + public abstract void teardown(); +} diff --git a/lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java similarity index 100% rename from lib/scala/logging-server/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java rename to lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceAlreadySetup.java diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java new file mode 100644 index 000000000000..85c844af134c --- /dev/null +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java @@ -0,0 +1,38 @@ +package org.enso.logging; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import java.net.URI; + +public abstract class LoggingServiceFactory { + + public abstract LoggingService localServerFor(int port); + + @SuppressWarnings("unchecked") + public static LoggingServiceFactory get() { + if (_loggingServiceFactory == null) { + Config c = ConfigFactory.load("enso-logging.conf"); + if (c.hasPath(implClassKey)) { + try { + String clazzName = c.getString(implClassKey); + Class clazz = Class.forName(clazzName); + _loggingServiceFactory = + (LoggingServiceFactory) clazz.getDeclaredConstructor().newInstance(); + } catch (Throwable e) { + e.printStackTrace(); + System.err.println("Failed to initialize LoggingServiceFactory configuration class"); + return null; + } + + } else { + System.err.println("Missing log configuration class key: " + implClassKey); + return null; + } + } + return _loggingServiceFactory; + } + + private static LoggingServiceFactory _loggingServiceFactory = null; + + private static final String implClassKey = LoggingServiceFactory.class.getName() + ".impl.class"; +} diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala similarity index 80% rename from lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala rename to lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala index 014e82107f7e..94191eabad57 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala @@ -11,8 +11,8 @@ import scala.concurrent.ExecutionContext object LoggingServiceManager { - private[this] var loggingService: LoggingService = null - private[this] var currentLevel: Level = Level.TRACE + private[this] var loggingService: LoggingService[_] = null + private[this] var currentLevel: Level = Level.TRACE def currentLogLevelForThisApplication(): Level = currentLevel @@ -27,7 +27,7 @@ object LoggingServiceManager { throw new LoggingServiceAlreadySetup() } else { currentLevel = logLevel - val server = new LoggingServer(port) + val server = LoggingServiceFactory.get().localServerFor(port);; loggingService = server Future { server.start(logLevel, logPath, logFileSuffix, appender) diff --git a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala similarity index 82% rename from lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala rename to lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala index 7979ee91ac33..b07068438f4b 100644 --- a/lib/scala/logging-server/src/main/scala/org/enso/logging/LoggingSetupHelper.scala +++ b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala @@ -1,7 +1,6 @@ package org.enso.logging -import org.enso.logger.LogbackSetup -import org.enso.logger.config.LoggerSetup +import org.enso.logger.config.{LoggerSetup, LoggerSetupFactory} import org.slf4j.event.Level import java.net.URI @@ -31,11 +30,16 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { * Some logs may be added while inferring the parameters of logging infrastructure, leading to catch-22 situations. */ def initLogger(): Unit = { - LogbackSetup.get().setupNoOpAppender() + LoggerSetupFactory.get().setupNoOpAppender() } - /** Starts a logging server that collects logs from different components and immediate sets up logs from this component - * to send logs to it. + def setupFallback(): Unit = { + LoggerSetupFactory.get().setupConsoleAppender(defaultLogLevel) + } + + /** Starts a logging server that collects logs from different components. + * Once started, all logs are being forwarded to the server. + * * @param logLevel * @param logMasking */ @@ -43,7 +47,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { logLevel: Option[Level], logMasking: Boolean ): Unit = { - val loggerSetup = LogbackSetup.get() + val loggerSetup = LoggerSetupFactory.get() val config = loggerSetup.getConfig if (config.loggingServerNeedsBoot()) { val actualPort = config.getServer().port(); @@ -90,7 +94,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { logLevel, connectToExternalLogger, logMasking, - LogbackSetup.get() + LoggerSetupFactory.get() ); } @@ -110,20 +114,13 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { uri.getPort() ) if (!initialized) { - // Fallback, try the default appender if it is not the socket appender to the same port - val defaultAppender = loggerSetup.getConfig().getAppender() - if (!defaultAppender.isSameTargetAs(uri)) { - initialized = loggerSetup.setup(actualLogLevel) - if (!initialized) { - // Fallback to console - initialized = loggerSetup.setupConsoleAppender(actualLogLevel) - } - } else { + // Fallback + initialized = loggerSetup.setup(actualLogLevel) + if (!initialized) { // Fallback to console initialized = loggerSetup.setupConsoleAppender(actualLogLevel) } } - if (initialized) { Masking.setup(logMasking) loggingServiceEndpointPromise.success(None) @@ -145,7 +142,7 @@ abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { } def waitForSetup(): Unit = { - Await.ready(loggingServiceEndpointPromise.future, 5.seconds) + Await.ready(loggingServiceEndpointPromise.future, 10.seconds) } def tearDown(): Unit = { diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json index c695bde11fa4..b0bb93b4475d 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json @@ -565,5 +565,50 @@ "parameterTypes": ["java.lang.Boolean", "java.lang.Object"] } ] + }, + { + "name": "org.enso.logging.LogbackLoggingServiceFactory", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "name": "org.enso.logger.LogbackSetup", + "methods": [ + { + "name": "get" + } + ] + }, + { + "name":"ch.qos.logback.classic.pattern.DateConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LevelConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LoggerConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.MessageConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.IntegerTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json index 53c6ce2416be..05d5b61b4fc9 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json @@ -3,6 +3,7 @@ { "pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, { "pattern": "\\Qakka-http-version.conf\\E" }, { "pattern": "\\Qapplication.conf\\E" }, + { "pattern": "\\Qenso-logging.conf\\E" }, { "pattern": "\\Qorg/enso/projectmanager/boot/ProjectManager$.class\\E" }, { "pattern": "\\Qorg/enso/projectmanager/control/core/CovariantFlatMapOps.class\\E" @@ -51,6 +52,7 @@ "pattern": "\\Qorg/enso/projectmanager/service/versionmanagement/RuntimeVersionManagerMixin$ErrorRecovery.class\\E" }, { "pattern": "\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, + { "pattern": "\\Qch/qos/logback/classic/spi/Configurator.class\\E" }, { "pattern": "\\Qreference.conf\\E" }, { "pattern": "\\Qversion.conf\\E" }, { "pattern": "\\Qzio/App$$anon$1.class\\E" }, diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index 546931c2c71e..c3a66d6c18f0 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -25,9 +25,9 @@ logging-service { appenders = [ { name = "socket" - hostname = ${?LOGSERVER_HOSTNAME} + hostname = ${?ENSO_LOGSERVER_HOSTNAME} hostname = "localhost" - port = ${?LOGSERVER_PORT} + port = ${?ENSO_LOGSERVER_PORT} port = 6000 }, { @@ -37,18 +37,17 @@ logging-service { name = "console" } ] - default-appender = ${?DEFAULT_LOGGER} + default-appender = ${?ENSO_DEFAULT_APPENDER} default-appender = socket server { - start = ${?LOGSERVER_START} start = true - hostname = ${?LOGSERVER_HOSTNAME} - hostname = "localhost" - port = ${?LOGSERVER_PORT} + start = ${?ENSO_LOGSERVER_START} port = 6000 + port = ${?ENSO_LOGSERVER_PORT} + appender { - name = ${?LOGSERVER_APPENDER} name = "file" # file/console/socket/sentry + name = ${?ENS_LOGSERVER_APPENDER} rolling-policy { max-file-size = "100MB" max-history = 30 diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala index 2329f447e8c9..708b70cbe35d 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/infrastructure/languageserver/ExecutorWithUnlimitedPool.scala @@ -75,7 +75,6 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor { val inheritedLogLevel = LoggingServiceManager.currentLogLevelForThisApplication() - //LoggingCollector.currentLogLevelForThisApplication() val options = LanguageServerOptions( rootId = descriptor.rootId, interface = descriptor.networkConfig.interface, From e87b8a82a1677800aa510e1b55a808b782dce493 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 11:57:24 +0200 Subject: [PATCH 17/31] Scala -> Java --- build.sbt | 6 +- docs/infrastructure/logging.md | 33 ++++ .../enso/launcher/cli/LauncherLogging.scala | 2 +- .../launcher/upgrade/LauncherUpgrader.scala | 2 +- .../org/enso/logger/config/FileAppender.java | 10 ++ .../org/enso/logger/config/LoggingServer.java | 19 ++- .../java/org/enso/logger/LogbackSetup.java | 4 +- .../enso/logging/LoggingServiceManager.java | 46 ++++++ .../org/enso/logging/LoggingSetupHelper.java | 153 ++++++++++++++++++ .../enso/logging/LoggingServiceManager.scala | 44 ----- .../org/enso/logging/LoggingSetupHelper.scala | 151 ----------------- .../scala/org/enso/logger/TestLogger.scala | 14 +- .../src/main/resources/application.conf | 27 ++-- .../enso/projectmanager/boot/Logging.scala | 2 +- .../src/test/resources/application.conf | 2 +- 15 files changed, 295 insertions(+), 220 deletions(-) create mode 100644 lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java create mode 100644 lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala delete mode 100644 lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala diff --git a/build.sbt b/build.sbt index 89ce763ec5d3..d04d37c6d0d3 100644 --- a/build.sbt +++ b/build.sbt @@ -763,9 +763,9 @@ lazy val filewatcher = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "io.methvin" % "directory-watcher" % directoryWatcherVersion, - "commons-io" % "commons-io" % commonsIoVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test + "io.methvin" % "directory-watcher" % directoryWatcherVersion, + "commons-io" % "commons-io" % commonsIoVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test ) ++ logbackPkg.map(_ % Test) ) .dependsOn(testkit % Test) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index 600eef4b1612..9090d1023876 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -29,6 +29,39 @@ components. +## Configuration + +Each of the main components can customize logging format and output via section in `application.conf` configuration file. +The configuration is based on the SLF4J style of configuration and formatting but no SLF4J-specific configuration is present in the implementation. +During component's setup, its `application.conf` file is read, parsed and verified and available as part of the +class hierarchy defined in `org.enso.logger.config` package. +Class `org.enso.logger.config.LoggingServiceConfig` encapsulates the full information represented by the `logging-service` field of the configuration file. + +### Appenders + +Configuration can defined numerous logging "appenders" (or "handlers" in `java.util.logging` terminology) which specify the final format of the logging events and where to store/forward/display them. +Currently supported are +- console appender - the most basic appender that prints log events to stdout +- file appender - appender that writes log events to a file, with optional rolling file policy +- socket appender - appender that forwards log events to some logging server +- sentry appender - appender that forwards log events to a sentry.io service + +### Format + +Appenders that store/display log events can specify the format of the log message via `pattern` field e.g. +```typescript + + appenders = [ + { + name = "console" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex" + } + ... + ] +``` + +The pattern follows the classic's [PatternLayout](https://logback.qos.ch/manual/layouts.html#ClassicPatternLayout) pattern. + ## Protocol The service relies on a WebSocket connection to a specified endpoint that diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 0aa9bd79c2ee..4d7df0b31c4c 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -9,7 +9,7 @@ import scala.concurrent.ExecutionContext.Implicits.global /** Manages setting up the logging service within the launcher. */ -object LauncherLogging extends LoggingSetupHelper { +object LauncherLogging extends LoggingSetupHelper(global) { /** @inheritdoc */ override val defaultLogLevel: Level = Level.WARN diff --git a/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala b/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala index b21a07a7cf7a..18e9419daf5a 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/upgrade/LauncherUpgrader.scala @@ -430,7 +430,7 @@ object LauncherUpgrader { ): Int = { val logger = LoggerFactory.getLogger( classOf[LauncherUpgrader] - ) //.enter("auto-upgrade") + ) val globalCLIOptions = cachedCLIOptions.getOrElse( throw new IllegalStateException( "Upgrade requested but application was not initialized properly." diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java index e2ac7f07677f..05baa1344ce4 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -110,6 +110,16 @@ private static String stringWithDefault(Config c, String key, String defaultValu else return defaultValue; } + @Override + public String toString() { + return "file-appender: pattern - " + + pattern + + ", immediate-flush - " + + immediateFlush + + ", rolling-policy - " + + (rollingPolicy == null ? "no" : rollingPolicy.toString()); + } + // Config keys private static final String immediateFlushKey = "immediate-flush"; private static final String appendKey = "append"; diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java index c729bf393218..c3bfc522a766 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggingServer.java @@ -2,6 +2,10 @@ import com.typesafe.config.Config; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Configuration for the local server that collects logs from different services. * @@ -9,13 +13,22 @@ * @param appender appender's configuration describing how to transform received log events * @param start if true, will be started by the service defining the configuration */ -public record LoggingServer(int port, Appender appender, Boolean start) { +public record LoggingServer(int port, Map appenders, String appender, Boolean start) { public static LoggingServer parse(Config config) throws MissingConfigurationField { int port = config.getInt("port"); - Appender appender = Appender.parse(config.getConfig("appender")); + + Map appendersMap = new HashMap<>(); + if (config.hasPath("appenders")) { + List configs = config.getConfigList("appenders"); + for (Config c : configs) { + Appender a = Appender.parse(c); + appendersMap.put(a.getName(), a); + } + } + String defaultAppender = config.getString("default-appender"); boolean start = config.getBoolean("start"); - return new LoggingServer(port, appender, start); + return new LoggingServer(port, appendersMap, defaultAppender, start); } } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java index e8e4714b9d6e..dc4070776512 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -179,8 +179,8 @@ public boolean setupFileAppender( fileAppender.setFile(fullFilePath); } - fileAppender.setAppend(true); - fileAppender.setImmediateFlush(true); + fileAppender.setAppend(appenderConfig.isAppend()); + fileAppender.setImmediateFlush(appenderConfig.isImmediateFlush()); fileAppender.setEncoder(encoder); diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java new file mode 100644 index 000000000000..66e294e344c5 --- /dev/null +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java @@ -0,0 +1,46 @@ +package org.enso.logging; + +import java.net.URI; +import java.nio.file.Path; +import org.enso.logger.config.Appender; +import org.enso.logger.config.LoggingServer; +import org.slf4j.event.Level; +import scala.concurrent.ExecutionContext; +import scala.concurrent.Future; + +public class LoggingServiceManager { + private static LoggingService loggingService = null; + private static Level currentLevel = Level.TRACE; + + public static Level currentLogLevelForThisApplication() { + return currentLevel; + } + + public static Future setupServer( + Level logLevel, + int port, + Path logPath, + String logFileSuffix, + LoggingServer config, + ExecutionContext ec) { + if (loggingService != null) { + throw new LoggingServiceAlreadySetup(); + } else { + if (config.appenders().containsKey(config.appender())) { + currentLevel = logLevel; + var server = LoggingServiceFactory.get().localServerFor(port); + loggingService = server; + Appender appender = config.appenders().get(config.appender()); + return Future.apply(() -> server.start(logLevel, logPath, logFileSuffix, appender), ec); + } else { + throw new LoggerInitializationFailed(); + } + } + } + + public static void teardown() { + if (loggingService != null) { + loggingService.teardown(); + } + } +} diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java new file mode 100644 index 000000000000..4be16089e429 --- /dev/null +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java @@ -0,0 +1,153 @@ +package org.enso.logging; + +import java.net.URI; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.enso.logger.config.LoggerSetup; +import org.enso.logger.config.LoggerSetupFactory; +import org.enso.logger.config.MissingConfigurationField; +import org.enso.logger.masking.Masking; +import org.slf4j.event.Level; +import scala.Option; +import scala.Unit$; +import scala.concurrent.Await; +import scala.concurrent.ExecutionContext; +import scala.concurrent.Future; +import scala.concurrent.Promise; +import scala.concurrent.Promise$; +import scala.concurrent.duration.Duration$; + +/** + * Base class for any Enso service that needs to setup its logging. + * + *

Note: if this looks ugly and not very Java-friendly, it's because it is. It's a 1:1 + * translation from Scala. + */ +public abstract class LoggingSetupHelper { + + public LoggingSetupHelper(ExecutionContext ec) { + this.ec = ec; + } + + private ExecutionContext ec; + + protected abstract Level defaultLogLevel(); + + protected abstract String logFileSuffix(); + + protected abstract Path logPath(); + + public Future> loggingServiceEndpoint() { + return loggingServiceEndpointPromise.future(); + } + + private Promise> loggingServiceEndpointPromise = Promise$.MODULE$.apply(); + + /** + * Initialize logging to console prior to establishing logging. Some logs may be added while + * inferring the parameters of logging infrastructure, leading to catch-22 situations. + */ + public void initLogger() { + LoggerSetupFactory.get().setupNoOpAppender(); + } + + public void setupFallback() { + LoggerSetupFactory.get().setupConsoleAppender(defaultLogLevel()); + } + + /** + * Starts a logging server that collects logs from different components. Once started, all logs + * are being forwarded to the server. + * + * @param logLevel + * @param logMasking + */ + public void setupServerAndForwardLogs(Option logLevel, boolean logMasking) + throws MissingConfigurationField { + var loggerSetup = LoggerSetupFactory.get(); + var config = loggerSetup.getConfig(); + if (config.loggingServerNeedsBoot()) { + int actualPort = config.getServer().port(); + Level actualLogLevel = logLevel.getOrElse(() -> defaultLogLevel()); + LoggingServiceManager.setupServer( + actualLogLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) + .onComplete( + (result) -> { + try { + if (result.isFailure()) { + setup(logLevel, Option.empty(), logMasking, loggerSetup); + } else { + URI uri = result.get(); + Masking.setup(logMasking); + if (!loggerSetup.setup(actualLogLevel)) { + LoggingServiceManager.teardown(); + loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()); + } else { + loggingServiceEndpointPromise.success(Option.apply(uri)); + } + } + return Unit$.MODULE$; + } catch (MissingConfigurationField e) { + throw new RuntimeException(e); + } + }, + ec); + } else { + // Setup logger according to config + var actualLogLevel = logLevel.getOrElse(() -> defaultLogLevel()); + if (loggerSetup.setup(actualLogLevel)) { + loggingServiceEndpointPromise.success(Option.empty()); + } + } + } + + public void setup(Option logLevel, Option connectToExternalLogger, boolean logMasking) + throws MissingConfigurationField { + setup(logLevel, connectToExternalLogger, logMasking, LoggerSetupFactory.get()); + } + + public void setup( + Option logLevel, + Option connectToExternalLogger, + boolean logMasking, + LoggerSetup loggerSetup) + throws MissingConfigurationField { + var actualLogLevel = logLevel.getOrElse(() -> defaultLogLevel()); + if (connectToExternalLogger.isDefined()) { + var uri = connectToExternalLogger.get(); + var initialized = + loggerSetup.setupSocketAppender(actualLogLevel, uri.getHost(), uri.getPort()); + if (!initialized) { + // Fallback + initialized = loggerSetup.setup(actualLogLevel); + if (!initialized) { + // Fallback to console + initialized = loggerSetup.setupConsoleAppender(actualLogLevel); + } + } + if (initialized) { + Masking.setup(logMasking); + loggingServiceEndpointPromise.success(Option.empty()); + } else { + loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()); + } + } else { + if (loggerSetup.setup(actualLogLevel)) { + Masking.setup(logMasking); + loggingServiceEndpointPromise.success(Option.empty()); + } else { + loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()); + } + } + } + + public void waitForSetup() throws InterruptedException, TimeoutException { + Await.ready( + loggingServiceEndpointPromise.future(), Duration$.MODULE$.apply(5, TimeUnit.SECONDS)); + } + + public void tearDown() { + LoggingServiceManager.teardown(); + } +} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala deleted file mode 100644 index 94191eabad57..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingServiceManager.scala +++ /dev/null @@ -1,44 +0,0 @@ -package org.enso.logging - -import org.enso.logger.config.Appender - -import java.net.URI -import org.slf4j.event.Level - -import java.nio.file.Path -import scala.concurrent.Future -import scala.concurrent.ExecutionContext - -object LoggingServiceManager { - - private[this] var loggingService: LoggingService[_] = null - private[this] var currentLevel: Level = Level.TRACE - - def currentLogLevelForThisApplication(): Level = currentLevel - - def setupServer( - logLevel: Level, - port: Int, - logPath: Path, - logFileSuffix: String, - appender: Appender - )(implicit ec: ExecutionContext): Future[URI] = { - if (loggingService != null) { - throw new LoggingServiceAlreadySetup() - } else { - currentLevel = logLevel - val server = LoggingServiceFactory.get().localServerFor(port);; - loggingService = server - Future { - server.start(logLevel, logPath, logFileSuffix, appender) - } - } - } - - def teardown(): Unit = { - if (loggingService != null) { - loggingService.teardown() - } - } - -} diff --git a/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala b/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala deleted file mode 100644 index b07068438f4b..000000000000 --- a/lib/scala/logging-service/src/main/scala/org/enso/logging/LoggingSetupHelper.scala +++ /dev/null @@ -1,151 +0,0 @@ -package org.enso.logging - -import org.enso.logger.config.{LoggerSetup, LoggerSetupFactory} -import org.slf4j.event.Level - -import java.net.URI -import java.nio.file.Path -import scala.concurrent.Future -import scala.concurrent.ExecutionContext -import scala.concurrent.Promise -import scala.util.{Failure, Success} -import org.enso.logger.masking.Masking - -import scala.concurrent.Await -import scala.concurrent.duration.DurationInt - -abstract class LoggingSetupHelper(implicit executionContext: ExecutionContext) { - val defaultLogLevel: Level - - val logFileSuffix: String - - def logPath: Path - - def loggingServiceEndpoint(): Future[Option[URI]] = - loggingServiceEndpointPromise.future - - private val loggingServiceEndpointPromise = Promise[Option[URI]]() - - /** Initialize logging to console prior to establishing logging. - * Some logs may be added while inferring the parameters of logging infrastructure, leading to catch-22 situations. - */ - def initLogger(): Unit = { - LoggerSetupFactory.get().setupNoOpAppender() - } - - def setupFallback(): Unit = { - LoggerSetupFactory.get().setupConsoleAppender(defaultLogLevel) - } - - /** Starts a logging server that collects logs from different components. - * Once started, all logs are being forwarded to the server. - * - * @param logLevel - * @param logMasking - */ - def setupServerAndForwardLogs( - logLevel: Option[Level], - logMasking: Boolean - ): Unit = { - val loggerSetup = LoggerSetupFactory.get() - val config = loggerSetup.getConfig - if (config.loggingServerNeedsBoot()) { - val actualPort = config.getServer().port(); - val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LoggingServiceManager - .setupServer( - actualLogLevel, - actualPort, - logPath, - logFileSuffix, - config.getServer().appender() - ) - .onComplete { - case Failure(_) => - // fallback to the default appender - setup(logLevel, None, logMasking, loggerSetup) - case Success(uri) => - Masking.setup(logMasking) - val result = loggerSetup.setup(actualLogLevel) - if (!result) { - LoggingServiceManager.teardown() - loggingServiceEndpointPromise.failure( - new LoggerInitializationFailed() - ) - } else { - loggingServiceEndpointPromise.success(Some(uri)) - } - } - } else { - // Setup logger according to config - val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - if (loggerSetup.setup(actualLogLevel)) { - loggingServiceEndpointPromise.success(None) - } - } - } - - def setup( - logLevel: Option[Level], - connectToExternalLogger: Option[URI], - logMasking: Boolean - ): Unit = { - setup( - logLevel, - connectToExternalLogger, - logMasking, - LoggerSetupFactory.get() - ); - } - - def setup( - logLevel: Option[Level], - connectToExternalLogger: Option[URI], - logMasking: Boolean, - loggerSetup: LoggerSetup - ): Unit = { - val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - - connectToExternalLogger match { - case Some(uri) => - var initialized = loggerSetup.setupSocketAppender( - actualLogLevel, - uri.getHost(), - uri.getPort() - ) - if (!initialized) { - // Fallback - initialized = loggerSetup.setup(actualLogLevel) - if (!initialized) { - // Fallback to console - initialized = loggerSetup.setupConsoleAppender(actualLogLevel) - } - } - if (initialized) { - Masking.setup(logMasking) - loggingServiceEndpointPromise.success(None) - } else { - loggingServiceEndpointPromise.failure( - new LoggerInitializationFailed() - ) - } - case None => - if (loggerSetup.setup(actualLogLevel)) { - Masking.setup(logMasking) - loggingServiceEndpointPromise.success(None) - } else { - loggingServiceEndpointPromise.failure( - new LoggerInitializationFailed() - ) - } - } - } - - def waitForSetup(): Unit = { - Await.ready(loggingServiceEndpointPromise.future, 10.seconds) - } - - def tearDown(): Unit = { - LoggingServiceManager.teardown() - } -} diff --git a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala index 975ebc66dc1c..1d53ae68de6f 100644 --- a/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala +++ b/lib/scala/logging-utils/src/test/scala/org/enso/logger/TestLogger.scala @@ -6,14 +6,20 @@ import ch.qos.logback.classic.LoggerContext object TestLogger { + /** Gathers all logs of a specified type while executing a closure. + * + * @param of class of logs to collect + * @param action a generic closure to execute + * @tparam T the return type of executing a closure + * @tparam A the type of logs to collect + * @return a tuple with the result of executing the closure and the list of log events collected + */ def gather[T, A](of: Class[A], action: => T): (T, List[TestLogMessage]) = { - val logger = LoggerFactory.getLogger(of).asInstanceOf[Logger] - //val (_, logs) = TestLogger.gatherLogs { + val logger = LoggerFactory.getLogger(of).asInstanceOf[Logger] val appender = new TestAppender() appender.setContext( LoggerFactory.getILoggerFactory().asInstanceOf[LoggerContext] - ); - + ) logger.addAppender(appender) appender.start() val result = action diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index c3a66d6c18f0..d045dc605c46 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -45,15 +45,24 @@ logging-service { port = 6000 port = ${?ENSO_LOGSERVER_PORT} - appender { - name = "file" # file/console/socket/sentry - name = ${?ENS_LOGSERVER_APPENDER} - rolling-policy { - max-file-size = "100MB" - max-history = 30 - max-total-size = "2GB" - } - } + + appenders = [ + { + name = "file" # file/console/socket/sentry + rolling-policy { + max-file-size = "100MB" + max-history = 30 + max-total-size = "2GB" + } + }, + { + name = "sentry" + dsn = ${?ENSO_SENTRY_APPENDER_DSN} + dsn = "" + } + ] + default-appender = ${?ENSO_LOGSERVER_APPENDER} + default-appender = file } } diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala index 93985c3fe9bd..a2b7b649a18f 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/Logging.scala @@ -12,7 +12,7 @@ import org.enso.logging.LoggingSetupHelper import scala.concurrent.ExecutionContext.Implicits.global /** A helper for setting up the logging service in the Project Manager. */ -object Logging extends LoggingSetupHelper { +object Logging extends LoggingSetupHelper(global) { /** @inheritdoc */ override val defaultLogLevel: Level = Level.INFO diff --git a/lib/scala/project-manager/src/test/resources/application.conf b/lib/scala/project-manager/src/test/resources/application.conf index 29d57dbfec33..b1a7f8f0c7ac 100644 --- a/lib/scala/project-manager/src/test/resources/application.conf +++ b/lib/scala/project-manager/src/test/resources/application.conf @@ -12,7 +12,7 @@ logging-service { appenders = [ { name = "console" - pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] default-appender = console From b142d4017888216119c208d39e0839602e025e21 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 17:08:03 +0200 Subject: [PATCH 18/31] Minor simplifications --- .../websocket/json/BaseServerTest.scala | 4 +- .../launcher/cli/LauncherApplication.scala | 1 - .../enso/launcher/cli/LauncherLogging.scala | 4 +- .../scala/org/enso/runner/RunnerLogging.scala | 6 +-- .../enso/logger/{config => }/LoggerSetup.java | 41 ++++++++++++++++++- .../java/org/enso/logger/config/Appender.java | 1 + .../enso/logger/config/ConsoleAppender.java | 1 + .../org/enso/logger/config/FileAppender.java | 1 + .../logger/config/LoggerSetupFactory.java | 34 --------------- .../enso/logger/config/SentryAppender.java | 1 + .../enso/logger/config/SocketAppender.java | 1 + .../java/org/enso/logger/LogbackSetup.java | 17 +------- .../src/main/resources/enso-logging.conf | 2 +- .../org/enso/logging/LoggingSetupHelper.java | 30 +++++++++----- .../enso/projectmanager/BaseServerSpec.scala | 6 +-- .../LanguageServerSupervisorSpec.scala | 4 +- 16 files changed, 79 insertions(+), 75 deletions(-) rename lib/scala/logging-config/src/main/java/org/enso/logger/{config => }/LoggerSetup.java (72%) delete mode 100644 lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala index 43d19e17b13f..1d9afa4682bf 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/BaseServerTest.scala @@ -45,7 +45,7 @@ import org.enso.languageserver.vcsmanager.{Git, VcsManager} import org.enso.librarymanager.LibraryLocations import org.enso.librarymanager.local.DefaultLocalLibraryProvider import org.enso.librarymanager.published.PublishedLibraryCache -import org.enso.logger.LogbackSetup +import org.enso.logger.LoggerSetup import org.enso.pkg.PackageManager import org.enso.polyglot.data.TypeGraph import org.enso.polyglot.runtime.Runtime.Api @@ -75,7 +75,7 @@ class BaseServerTest val timeout: FiniteDuration = 10.seconds - LogbackSetup.get().setup() + LoggerSetup.get().setup() def isFileWatcherEnabled: Boolean = false diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index 58d7e1de27e5..1c38d5f45cc6 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -674,7 +674,6 @@ object LauncherApplication { connectLogger, !disableLogMasking ) - LauncherLogging.waitForSetup() initializeApp() if (version) { diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala index 4d7df0b31c4c..a49ebdc435f5 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherLogging.scala @@ -2,7 +2,7 @@ package org.enso.launcher.cli import java.nio.file.Path import org.enso.launcher.distribution.DefaultManagers -import org.enso.logger.config.LoggerSetupFactory +import org.enso.logger.LoggerSetup import org.slf4j.event.Level import org.enso.logging.LoggingSetupHelper import scala.concurrent.ExecutionContext.Implicits.global @@ -33,6 +33,6 @@ object LauncherLogging extends LoggingSetupHelper(global) { def prepareForUninstall(logLevel: Option[Level]): Unit = { waitForSetup() val actualLogLevel = logLevel.getOrElse(defaultLogLevel) - LoggerSetupFactory.get().setupConsoleAppender(actualLogLevel) + LoggerSetup.get().setupConsoleAppender(actualLogLevel) } } diff --git a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala index e81e97429eb1..ccaee4120a60 100644 --- a/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala +++ b/engine/runner/src/main/scala/org/enso/runner/RunnerLogging.scala @@ -2,7 +2,7 @@ package org.enso.runner import java.net.URI import com.typesafe.scalalogging.Logger -import org.enso.logger.config.LoggerSetupFactory +import org.enso.logger.LoggerSetup import org.enso.logger.masking.Masking import org.slf4j.event.Level @@ -32,7 +32,7 @@ object RunnerLogging { ): Unit = { import scala.concurrent.ExecutionContext.Implicits.global Masking.setup(logMasking) - val loggerSetup = LoggerSetupFactory.get() + val loggerSetup = LoggerSetup.get() val initializedLogger = connectionUri match { case Some(uri) => Future { @@ -73,6 +73,6 @@ object RunnerLogging { /** Shuts down the logging service gracefully. */ def tearDown(): Unit = { - LoggerSetupFactory.get().teardown() + LoggerSetup.get().teardown() } } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java similarity index 72% rename from lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java rename to lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index a37d774bc72e..8b2c9690c27f 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -1,11 +1,48 @@ -package org.enso.logger.config; +package org.enso.logger; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; import java.nio.file.Path; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import org.enso.logger.config.LoggingServiceConfig; +import org.enso.logger.config.MissingConfigurationField; import org.slf4j.event.Level; /** Base class to be implemented by the underlying logging implementation. */ public abstract class LoggerSetup { + private static LoggerSetup _instance = null; + private static Object lock = new Object(); + public static LoggerSetup get() { + if (_instance == null) { + synchronized (lock) { + if (_instance == null) { + + Config c = ConfigFactory.load("enso-logging"); + //System.err.println(c.hasPath(implClassKey) + " and " + c.hasPath("akka")); + if (c.hasPath(implClassKey)) { + try { + String clazzName = c.getString(implClassKey); + Class clazz = Class.forName(clazzName); + _instance = (LoggerSetup) clazz.getConstructor().newInstance(); + } catch (Throwable e) { + e.printStackTrace(); + System.err.println("Failed to initialize LoggerSetup configuration class: " + e.getMessage()); + } + } else { + System.err.println("Missing log configuration class key:" + implClassKey); + } + } + } + } + return _instance; + } + + /** Returns parsed application config used to create this instance * */ public abstract LoggingServiceConfig getConfig(); @@ -89,4 +126,6 @@ public abstract boolean setup( /** Shuts down all loggers. */ public abstract void teardown(); + + private static final String implClassKey = LoggerSetup.class.getName() + ".impl.class"; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java index 9299401d9786..5446e6557af8 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -2,6 +2,7 @@ import com.typesafe.config.Config; import java.nio.file.Path; +import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; /** diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java index 0e69d25a29d8..52d86ea2c511 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java @@ -1,6 +1,7 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; /** Config for log configuration that appends to the console */ diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java index 05baa1344ce4..b7cbf69e75fb 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -3,6 +3,7 @@ import com.typesafe.config.Config; import java.nio.file.Path; import java.nio.file.Paths; +import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; /** Config for log configuration that appends to the file. */ diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java deleted file mode 100644 index e3ba17e0f590..000000000000 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/LoggerSetupFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.enso.logger.config; - -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; -import java.lang.reflect.Method; - -public class LoggerSetupFactory { - - private static LoggerSetup _loaded = null; - - public static LoggerSetup get() { - if (_loaded == null) { - Config c = ConfigFactory.load("enso-logging.conf"); - if (c.hasPath(implClassKey)) { - try { - String clazzName = c.getString(implClassKey); - Class clazz = Class.forName(clazzName); - Method meth = clazz.getDeclaredMethod("get"); - _loaded = (LoggerSetup) meth.invoke(null); - } catch (Throwable e) { - e.printStackTrace(); - System.err.println("Failed to initialize LoggerSetup configuration class"); - return null; - } - } else { - System.err.println("Missing log configuration class key:" + implClassKey); - return null; - } - } - return _loaded; - } - - private static final String implClassKey = LoggerSetup.class.getName() + ".impl.class"; -} diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java index 491290d6d6d5..99d660377804 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -1,6 +1,7 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; /** Config for log configuration that sends logs to sentry.io service. */ diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java index fe0f6f9a7551..2731c37eb47f 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -1,6 +1,7 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; /** Config for log configuration that forwards logs to the network socket as-is. */ diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java index dc4070776512..051bb47b62a5 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -27,26 +27,13 @@ import org.slf4j.event.Level; public class LogbackSetup extends LoggerSetup { - - private LogbackSetup(LoggingServiceConfig config, LoggerContext context) { this.config = config; this.context = context; } - private static LogbackSetup _setup = null; - private final static Object lock = new Object(); - - /** Get a singleton instance of LoggerSetup */ - public static LoggerSetup get() throws MissingConfigurationField { - if (_setup == null) { - synchronized (lock) { - if (_setup == null) { - _setup = new LogbackSetup(LoggingServiceConfig.parseConfig(), (LoggerContext) LoggerFactory.getILoggerFactory()); - } - } - } - return _setup; + public LogbackSetup() throws MissingConfigurationField { + this(LoggingServiceConfig.parseConfig(), (LoggerContext) LoggerFactory.getILoggerFactory()); } /** diff --git a/lib/scala/logging-logback/src/main/resources/enso-logging.conf b/lib/scala/logging-logback/src/main/resources/enso-logging.conf index b03bfeb1d6c3..1b08165b7356 100644 --- a/lib/scala/logging-logback/src/main/resources/enso-logging.conf +++ b/lib/scala/logging-logback/src/main/resources/enso-logging.conf @@ -1,2 +1,2 @@ org.enso.logging.LoggingServiceFactory.impl.class=org.enso.logging.LogbackLoggingServiceFactory -org.enso.logger.config.LoggerSetup.impl.class=org.enso.logger.LogbackSetup +org.enso.logger.LoggerSetup.impl.class=org.enso.logger.LogbackSetup diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java index 4be16089e429..11e652557642 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java @@ -4,8 +4,7 @@ import java.nio.file.Path; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.enso.logger.config.LoggerSetup; -import org.enso.logger.config.LoggerSetupFactory; +import org.enso.logger.LoggerSetup; import org.enso.logger.config.MissingConfigurationField; import org.enso.logger.masking.Masking; import org.slf4j.event.Level; @@ -49,23 +48,23 @@ public Future> loggingServiceEndpoint() { * inferring the parameters of logging infrastructure, leading to catch-22 situations. */ public void initLogger() { - LoggerSetupFactory.get().setupNoOpAppender(); + LoggerSetup.get().setupNoOpAppender(); } public void setupFallback() { - LoggerSetupFactory.get().setupConsoleAppender(defaultLogLevel()); + LoggerSetup.get().setupConsoleAppender(defaultLogLevel()); } /** - * Starts a logging server that collects logs from different components. Once started, all logs - * are being forwarded to the server. + * Starts a logging server, if necessary, that accepts logs from different components. + * Once started, logs in this service are being setup to be forwarded to that logging server. * - * @param logLevel - * @param logMasking + * @param logLevel maximal level of log events to be forwarded + * @param logMasking true if masking of sensitive data should be applied to all log messages */ public void setupServerAndForwardLogs(Option logLevel, boolean logMasking) throws MissingConfigurationField { - var loggerSetup = LoggerSetupFactory.get(); + var loggerSetup = LoggerSetup.get(); var config = loggerSetup.getConfig(); if (config.loggingServerNeedsBoot()) { int actualPort = config.getServer().port(); @@ -102,12 +101,21 @@ actualLogLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) } } + /** + * Initializes logging for this service using the URI of the dedicated logging server. + * If connecting to the logging server failed, or the optional address is missing, log events will be handled purely based on configuration packaged with this service. + * + * @param logLevel optional maximal level of log events that will be handled by the logging infrastructure + * @param connectToExternalLogger optional address of the logging server + * @param logMasking true if sensitive data should be masked in log events, false otherwise + * @throws MissingConfigurationField if the config file has been mis-configured + */ public void setup(Option logLevel, Option connectToExternalLogger, boolean logMasking) throws MissingConfigurationField { - setup(logLevel, connectToExternalLogger, logMasking, LoggerSetupFactory.get()); + setup(logLevel, connectToExternalLogger, logMasking, LoggerSetup.get()); } - public void setup( + private void setup( Option logLevel, Option connectToExternalLogger, boolean logMasking, diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala index 663b2a5d44f3..041f056e33bd 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/BaseServerSpec.scala @@ -16,7 +16,7 @@ import org.enso.editions.Editions import org.enso.cli.OS import org.enso.jsonrpc.test.JsonRpcServerTestKit import org.enso.jsonrpc.{ClientControllerFactory, ProtocolFactory} -import org.enso.logger.LogbackSetup +import org.enso.logger.LoggerSetup import org.enso.pkg.{Config, PackageManager} import org.enso.projectmanager.boot.Globals.{ConfigFilename, ConfigNamespace} import org.enso.projectmanager.boot.configuration._ @@ -237,9 +237,9 @@ class BaseServerSpec extends JsonRpcServerTestKit with BeforeAndAfterAll { super.beforeAll() if (debugLogs) { - LogbackSetup.get().setup(Level.TRACE) + LoggerSetup.get().setup(Level.TRACE) } else { - LogbackSetup.get().setup() + LoggerSetup.get().setup() } setupEditions() diff --git a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala index 5003105fc3e9..42e235881e86 100644 --- a/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala +++ b/lib/scala/project-manager/src/test/scala/org/enso/projectmanager/infrastructure/languageserver/LanguageServerSupervisorSpec.scala @@ -3,7 +3,7 @@ package org.enso.projectmanager.infrastructure.languageserver import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{ImplicitSender, TestActor, TestKit, TestProbe} import com.miguno.akka.testing.VirtualTime -import org.enso.logger.LogbackSetup +import org.enso.logger.LoggerSetup import org.enso.projectmanager.boot.configuration.SupervisionConfig import org.enso.projectmanager.infrastructure.http.AkkaBasedWebSocketConnectionFactory import org.enso.projectmanager.infrastructure.languageserver.LanguageServerBootLoader.ServerBooted @@ -107,7 +107,7 @@ class LanguageServerSupervisorSpec trait TestCtx { - LogbackSetup.get().setup() + LoggerSetup.get().setup() val VerificationTimeout = 120000 From 5c8ae56e66d533cfd76e0df4e508c869357e2e56 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 17:08:55 +0200 Subject: [PATCH 19/31] update documentation --- docs/infrastructure/logging.md | 452 +++++++++++++++------------------ 1 file changed, 199 insertions(+), 253 deletions(-) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index 9090d1023876..f08b808b18c6 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -10,283 +10,272 @@ order: 6 The Enso project features a centralised logging service to allow for the aggregation of logs from multiple components. This service can be started with -one of the main components, allowing other components connect to it. The service -aggregates all logs in one place for easier analysis of the interaction between -components. +one of the main components, allowing other components to connect to it. The +service aggregates all logs in one place for easier analysis of the interaction +between components. Components can also write to console, files directly without +involving an additional logging server. -- [Protocol](#protocol) - - [Types](#types) - - [Messages](#messages) - - [Examples](#examples) +- [Configuration](#configuration) + - [Custom Log Levels](#custom-log-levels) + - [Appenders](#appenders) + - [Format](#format) + - [File](#file-appender) + - [Network](#socket-appender) + - [Sentry.io](#sentry-appender) - [JVM Architecture](#jvm-architecture) - [SLF4J Interface](#slf4j-interface) - [Setting Up Logging](#setting-up-logging) - [Log Masking](#log-masking) - - [Configuration](#configuration) - [Logging in Tests](#logging-in-tests) ## Configuration -Each of the main components can customize logging format and output via section in `application.conf` configuration file. -The configuration is based on the SLF4J style of configuration and formatting but no SLF4J-specific configuration is present in the implementation. -During component's setup, its `application.conf` file is read, parsed and verified and available as part of the -class hierarchy defined in `org.enso.logger.config` package. -Class `org.enso.logger.config.LoggingServiceConfig` encapsulates the full information represented by the `logging-service` field of the configuration file. +The logging settings should be placed under the `logging-service` key of the +`application.conf` config. Each of the main components can customize format and +output target via section in `application.conf` configuration file. The +configuration is using HOCON-style, as defined by +[lightbend/config](https://github.com/lightbend/config). Individual values +accepted in the config are inspired by SLF4J's properties, formatting and +implementations. -### Appenders +The configuration has two main sections: -Configuration can defined numerous logging "appenders" (or "handlers" in `java.util.logging` terminology) which specify the final format of the logging events and where to store/forward/display them. -Currently supported are -- console appender - the most basic appender that prints log events to stdout -- file appender - appender that writes log events to a file, with optional rolling file policy -- socket appender - appender that forwards log events to some logging server -- sentry appender - appender that forwards log events to a sentry.io service +- [custom log levels](#custom-log-levels) +- [applications' appenders](#appenders) (also known as configuration of log + events output target) -### Format +During component's setup, its `application.conf` file is read, parsed and +verified and available as part of the class hierarchy defined in +`org.enso.logger.config` package. Class +`org.enso.logger.config.LoggingServiceConfig` encapsulates the complete +information represented by the `logging-service` key of the config file and is +used to programmatically initialize loggers. -Appenders that store/display log events can specify the format of the log message via `pattern` field e.g. -```typescript +As per [configuration schema](https://github.com/lightbend/config) any key can +have a default value that can be overridden by an environmental variable. For +example - appenders = [ - { - name = "console" - pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex" - } - ... - ] +``` + { + host = localhost + host = $ENSO_HOST + } ``` -The pattern follows the classic's [PatternLayout](https://logback.qos.ch/manual/layouts.html#ClassicPatternLayout) pattern. +defines a `host` key once, except that `ENSO_HOST` values takes a precedence if +it is defined during loading of the config file. -## Protocol +### Custom Log Levels -The service relies on a WebSocket connection to a specified endpoint that -exchanges JSON-encoded text messages. The communication is uni-directional - the -only messages are log messages that are sent from a connected client to the -server that aggregates the logs. +The `logging-service.logger` configuration provides an ability to override the +default application log level for particular loggers. In the `logger` subconfig +the key specifies the logger name (or it's prefix) and the value specifies the +log level for that logger. -### Types +``` +logging-service.logger { + akka.actor = info + akka.event = error + akka.io = error + slick { + jdbc.JdbcBackend.statement = debug + "*" = error + } +} +``` -##### `LogLevel` +For example, the config above limits all `akka.actor.*` loggers to the info +level logging, and `akka.event.*` loggers can emit only the error level +messages. -The log level encoded as a number. Possible values are: +Config supports globs (`*`). For example, the config above sets +`jdbc.JdbcBackend.statement` SQL statements logging to debug level, and the rest +of the slick loggers to error level. -- 0 - indicating `ERROR` level, -- 1 - indicating `WARN` level, -- 2 - indicating `INFO` level, -- 3 - indicating `DEBUG` level, -- 4 - indicating `TRACE` level. +Additionally, custom log events can be provided during runtime via system +properties, without re-packaging the updated config file. For example ```typescript -type LogLevel = 0 | 1 | 2 | 3 | 4; +akka.actor = info; ``` -##### `UTCTime` - -Message timestamp encoded as milliseconds elapsed from the UNIX epoch, i.e. -1970-01-01T00:00:00Z. +is equivalent to ```typescript -type UTCTime = number; + -Dakka.actor.Logger.level=info ``` -##### `Exception` +Any custom log level is therefore defined with `-Dx.y.Z.Logger.level` where `x`, +`y` and `Z` refer to the package elements and class name, respectively. System +properties always have a higher priority over those defined in the +`application.conf` file. -Encodes an exception that is related to a log message. +### Appenders -The `cause` field may be omitted if the exception does not have another -exception as its cause. +Log output target is also configured in the `application.conf` files in the +"appenders" section ("appender" is equivalent to `java.util.logging.Handler` +semantics). Each appender section can provide further required and optional +key/value pairs, to better customize the log target output. -```typescript -interface Exception { - // Name of the exception. In Java this can be the qualified classname. - name: String; - // Message associated with the exception. May be empty. - message: String; - // A stack trace indicating code location where the exception has originated - // from. May be empty if unavailable. - trace: [TraceElement]; - // Optional, another exception that caused this one. - cause?: Exception; -} -``` +Currently supported are -##### `TraceElement` +- console appender - the most basic appender that prints log events to stdout +- [file appender](#file-appender) - appender that writes log events to a file, + with optional rolling file policy +- [socket appender](#socket-appender) - appender that forwards log events to + some logging server +- [sentry.io appender](#sentry-appender) - appender that forwards log events to + a sentry.io service + +The appenders are defined by the `logging-service.appenders`. Currently only a +single appender can be selected at a time. The selection may also be done via an +environmental variable `$ENSO_DEFAULT_APPENDER`. + +#### Format + +The pattern follows the classic's +[PatternLayout](https://logback.qos.ch/manual/layouts.html#ClassicPatternLayout) +format. -Represents a single element of exception's stacktrace. +Appenders that store/display log events can specify the format of the log +message via `pattern` field e.g. ```typescript -interface TraceElement { - // Name of the stack location. For example, in Java this can be a qualified - // method name. - element: String; - // Code location of the element. - location: String; -} + + appenders = [ + { + name = "console" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex" + } + ... + ] ``` -In Java, the location is usually a filename and line number locating the code -that corresponds to the indicated stack location, for example `Main.java:123`. -Native methods may be handled differently, as well as code from different -languages, for example Enso also includes the columns - `Test.enso:4:3-19`. +#### File Appender -### Messages +File appender directs all log events to a log file: -Currently, the service supports only one message type - `LogMessage`, messages -not conforming to this format will be ignored. The first non-conforming message -for each connection will emit a warning. +``` + { + name = "file" + append = + immediate-flush = + pattern = + rolling-policy { + max-file-size = + max-history = + max-total-size = + } + } +``` -#### `LogMessage` +Rolling policy is a fully optional property of File Appender that would trigger +automatic log rotation. All properties are optional with some reasonable +defaults if not specified. -Describes the log message that the server should report and does not expect any -response. +#### Socket Appender -##### Parameters +Configuration -```typescript -{ - // Log level associated with the message. - level: LogLevel; - // Timestamp indicating when the message was sent. - time: UTCTime; - // An identifier of a log group - the group should indicate which component - // the message originated from and any (possibly nested) context. - group: String; - // The actual log message. - message: String; - // Optional exception associated with the message. - exception?: Exception; -} ``` - -The `exception` field may be omitted if there is no exception associated with -the message. - -In general, the `group` name can be arbitrary, but it is often the quallified -name of the class that the log message originates from and it is sometimes -extended with additional nested context, for example: - -- `org.enso.launcher.cli.Main` -- `org.enso.compiler.pass.analyse.AliasAnalysis.analyseType` - -### Examples - -For example, an error message with an attached exception may look like this (the -class names are made up): - -```json -{ - "level": 0, - "time": 1600864353151, - "group": "org.enso.launcher.Main", - "message": "Failed to load a configuration file.", - "exception": { - "name": "org.enso.componentmanager.config.ConfigurationLoaderFailure", - "message": "Configuration file does not exist.", - "trace": [ - { - "element": "org.enso.componentmanager.config.ConfigurationLoader.load", - "location": "ConfigurationLoader.scala:123" - }, - { - "element": "org.enso.launcher.Main", - "location": "Main.scala:42" - } - ], - "cause": { - "name": "java.io.FileNotFoundException", - "message": "config.yaml (No such file or directory)", - "trace": [] - } + { + name = "socket" + hostname = + port = } -} ``` -Another example could be an info message (without attached exceptions): +The two fields can be overridden via environment variables: -```json -{ - "level": 2, - "time": 1600864353151, - "group": "org.enso.launcher.Main", - "message": "Configuration file loaded successfully." -} +- `hostanme` has an equivalent `$ENSO_LOGSERVER_HOSTNAME` variable +- `port` has an equivalent `$ENSO_LOGSERVER_PORT` variable + +#### Sentry Appender + +``` + { + name = "sentry" + dsn = + } ``` +Sentry's Appender has a single required field, `dsn`. + ## JVM Architecture -A default implementation of both a client and server for the logger service are -provided for the JVM. +Previously, Enso came with its own implementation of Logger. That has been +replaced with a mostly off-the-shelf logging implementation, avoiding +maintenance costs for features that were already present in them. ### SLF4J Interface -The `logging-service` provides a class `org.enso.loggingservice.WSLogger` which -implements the `org.slf4j.Logger` interface, so it is compatible with all code -using SLF4J logging. When the `logging-service` is added to a project, it -automatically binds its logger instance as the SLF4J backend. So from the -perspective of the user, all that they have to do is use SLF4J compliant logging -in the application. - -One can use the `org.slf4j.LoggerFactory` directly, but for Scala code, it is -much better to use the `com.typesafe.scalalogging.Logger` which wraps the SLF4J -logger with macros that compute the log messages only if the given logging level -is enabled, and allows much prettier initialisation. Additionally, the -`logging-service` provides syntactic sugar for working with nested logging -contexts. +The user code must not be calling any of the underlying implementations, such as +Log4J or Logback, and should only request loggers via factory methods. -``` -package foo -import com.typesafe.scalalogging.Logger -import org.enso.logger.LoggerSyntax +One can use the `org.slf4j.LoggerFactory` directly to retrieve class-specific +logger. For Scala code, it is recommended to use the +`com.typesafe.scalalogging.Logger` instead which wraps the SLF4J logger with +macros that compute the log messages only if the given logging level is enabled, +and allows much prettier initialisation. -class Foo { - private val logger = Logger[Foo] +```java +package foo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - def bar(): Unit = { - logger.info("Hello world") // Logs `Hello world` from context `foo.Foo`. - baz() - } +public class Foo { + private Logger logger = LoggerFactory.getLogger(Foo.class); - def baz(): Unit = { - val bazLogger = logger.enter("baz") - bazLogger.warn("Inner") // Logs `Inner` from context `foo.Foo.baz` - } + public void bar() { + logger.info("Hello world!"); + } } ``` -The `enter` extension method follows the convention that each level of context -nesting is separated by `.`, much like package names. The root context is -usually the qualified name of the relevant class, but other components are free -to use other conventions if needed. - ### Setting Up Logging -The logger described above must know where it should send its logs, and this is -handled by the `LoggingServiceManager`. It allows to configure the logging -location, log level and setup the logging service in one of three different -modes: - -- _Server mode_, that will listen on a given port, gather both local and remote - logs and print them to stderr and to a file. -- _Client mode_, that will connect to a specified server and send all of its - logs there. It will not print anything. -- _Fallback mode_, that will just write the logs to stderr (and optionally) a - file, without setting up any services or connections. - -This logging mode initialization cannot usually happen at the time of static -initialization, since the connection details may depend on CLI arguments or -other configuration which may not be accessed immediately. To help with this, -the logger will buffer any log messages that are issued before the -initialization has happened and send them as soon as the service is initialized. - -In a rare situation where the service would not be initialized at all, a -shutdown hook is added that will print the pending log messages before exiting. -Some of the messages may be dropped, however, if more messages are buffered than -the buffer can hold. +The `org.slf4j.Logger` instances have to know where to send log events. This +setting is typically performed once, when the service starts, and applies +globally during its execution. Currently, it is not possible to dynamically +change where log events are being stored. The main class used for setting up +logging is `org.enso.logger.LoggerSetup` but it should not be instantiated +directly by the users. Instead, it should be retrieved with the Thread-safe +`org.enso.logger.LoggerSetup.get` factory method. `org.enso.logger.LoggerSetup` +provides a number of `setupXYZAppender` methods that will direct loggers to send +log events to an `XYZ` appender. Setting a specific hard-coded appender +programmatically should however be avoided by the users. Instead, one should +invoke one of the overloaded `setup` variants that initialize loggers based on +the provided `logging-service` configuration. + +```java +package foo; +import org.enso.logger.LoggerSetup; +import org.slf4j.event.Level; + +public class MyService { + + private Logger logger = LoggerFactory.getLogger(Foo.class); + ... + public void start(Level logLevel) { + LoggerSetup.get().setup(logLevel); + logger.info("My service is starting..."); + ... + } + ... +} +``` + +`org.enso.logging.LoggingSetupHelper` class was introduced to help with the most +common use cases - establishing a file-based logging in the Enso's dedicated +directories or connecting to an existing logging server once it starts accepting +connections. That is why services don't call `LoggerSetup` directly but instead +provide a service-specific implementation of +`org.enso.logging.LoggingSetupHelper`. `LoggingSetupHelper` and `LoggerSetup` +provide `teardown` methods to properly dispose of log events. ### Log Masking @@ -314,51 +303,8 @@ String interpolation in log statements `s"Created $obj"` should be avoided because it uses default `toString` implementation and can leak critical information even if the object implements custom interface for masked logging. -### Configuration - -The Logging Service settings should be placed under the `logging-service` key of -the `application.conf` config. - -The `logging-service.logger` configuration provides an ability to override the -default application log level for particular loggers. In the `logger` subconfig -the key specifies the logger name (or it's prefix) and the value specifies the -log level for that logger. - -``` -logging-service.logger { - akka.actor = info - akka.event = error - akka.io = error - slick { - jdbc.JdbcBackend.statement = debug - "*" = error - } -} -``` - -For example, the config above limits all `akka.actor.*` loggers to the info -level logging, and `akka.event.*` loggers can emit only the error level -messages. - -Config supports globs (`*`). For example, the config above sets -`jdbc.JdbcBackend.statement` SQL statements logging to debug level, and the rest -of the slick loggers to error level. - ### Logging in Tests -The Logging Service provides several utilities for managing logs inside of -tests. - -The primary method for setting log-level for all tests in a project is by -creating an `application.conf` file in `resources` of the `test` target with the -configuration key `logging-service.test-log-level` which should be set to a log -level name (possible values are: `off`, `error`, `warning`, `info`, `debug`, -`trace`). If this key is set to any value, the default logging queue is replaced -with a special test queue which handles the log messages depending on status of -the service. If a service has been set up, it just forwards them (so tests can -easily override the log handling). However if it has not been set up, the -enabled log messages are printed to STDERR and the rest is dropped. - -Another useful tool is `TestLogger.gatherLogs` - a function that wraps an action -and will return a sequence of logs reported when performing that action. It can -be used to verify logs of an action inside of a test. +The Logging Service provides a helper function `TestLogger.gatherLogs` that will +execute the closure and collect all logs reported in the specified class. That +way it can verify that all logs are being reported within the provided code. From 25ee77226a30d7a00075f86dc5ad23630184ece7 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 17:31:12 +0200 Subject: [PATCH 20/31] Tweaks to native image config Caused by the previous moving of classes. --- .../native-image/org/enso/launcher/reflect-config.json | 3 ++- .../META-INF/native-image/org/enso/runner/reflect-config.json | 3 ++- .../native-image/org/enso/projectmanager/reflect-config.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json index af842993928a..eec83d9bb71e 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json @@ -736,7 +736,8 @@ "name": "org.enso.logger.LogbackSetup", "methods": [ { - "name": "get" + "name": "", + "parameterTypes": [] } ] } diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index 0518da90f17e..459c46535994 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -240,7 +240,8 @@ "name": "org.enso.logger.LogbackSetup", "methods": [ { - "name": "get" + "name": "", + "parameterTypes": [] } ] }, diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json index b0bb93b4475d..ed8a50abc589 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json @@ -579,7 +579,8 @@ "name": "org.enso.logger.LogbackSetup", "methods": [ { - "name": "get" + "name": "", + "parameterTypes": [] } ] }, From 5d851edcae84c7235ca878cc634876f2e5f8237d Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 23:37:02 +0200 Subject: [PATCH 21/31] post-rebase tweaks --- .../src/main/java/org/enso/logger/LogbackSetup.java | 5 ++--- .../project-manager/src/main/resources/application.conf | 5 +++-- .../scala/org/enso/projectmanager/boot/ProjectManager.scala | 5 ----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java index 051bb47b62a5..b625340af6ad 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -181,8 +181,7 @@ public boolean setupFileAppender( @Override public boolean setupConsoleAppender(Level logLevel) { - LoggerAndContext env = contextInit(logLevel, config); - + LoggerAndContext env = contextInit(logLevel, config); org.enso.logger.config.ConsoleAppender appenderConfig = config.getConsoleAppender(); final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); try { @@ -198,7 +197,7 @@ public boolean setupConsoleAppender(Level logLevel) { consoleAppender.setEncoder(encoder); env.finalizeAppender(consoleAppender); - return true; + return true; } @Override diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index d045dc605c46..3917248b8907 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -44,8 +44,6 @@ logging-service { start = ${?ENSO_LOGSERVER_START} port = 6000 port = ${?ENSO_LOGSERVER_PORT} - - appenders = [ { name = "file" # file/console/socket/sentry @@ -59,6 +57,9 @@ logging-service { name = "sentry" dsn = ${?ENSO_SENTRY_APPENDER_DSN} dsn = "" + }, + { + name = "console" } ] default-appender = ${?ENSO_LOGSERVER_APPENDER} 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 7a0c8db2a0be..b4741e73a87f 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 @@ -267,11 +267,6 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { case 1 => Level.DEBUG case _ => Level.TRACE } - - // TODO [RW] at some point we may want to allow customization of color - // output in CLI flags - val colorMode = ColorMode.Auto - ZIO .attempt { Logging.initLogger() From 9e22fd9a19c1394ad3a27c210ede8bcdc633e8f4 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 25 Aug 2023 23:44:21 +0200 Subject: [PATCH 22/31] fmt --- .../src/main/java/org/enso/logger/LoggerSetup.java | 13 ++++--------- .../java/org/enso/logging/LoggingSetupHelper.java | 12 +++++++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index 8b2c9690c27f..2cd5fe5682f1 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -1,13 +1,8 @@ package org.enso.logger; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.file.Path; - import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; +import java.nio.file.Path; import org.enso.logger.config.LoggingServiceConfig; import org.enso.logger.config.MissingConfigurationField; import org.slf4j.event.Level; @@ -17,13 +12,13 @@ public abstract class LoggerSetup { private static LoggerSetup _instance = null; private static Object lock = new Object(); + public static LoggerSetup get() { if (_instance == null) { synchronized (lock) { if (_instance == null) { Config c = ConfigFactory.load("enso-logging"); - //System.err.println(c.hasPath(implClassKey) + " and " + c.hasPath("akka")); if (c.hasPath(implClassKey)) { try { String clazzName = c.getString(implClassKey); @@ -31,7 +26,8 @@ public static LoggerSetup get() { _instance = (LoggerSetup) clazz.getConstructor().newInstance(); } catch (Throwable e) { e.printStackTrace(); - System.err.println("Failed to initialize LoggerSetup configuration class: " + e.getMessage()); + System.err.println( + "Failed to initialize LoggerSetup configuration class: " + e.getMessage()); } } else { System.err.println("Missing log configuration class key:" + implClassKey); @@ -42,7 +38,6 @@ public static LoggerSetup get() { return _instance; } - /** Returns parsed application config used to create this instance * */ public abstract LoggingServiceConfig getConfig(); diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java index 11e652557642..4f0058b3ea18 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java @@ -56,8 +56,8 @@ public void setupFallback() { } /** - * Starts a logging server, if necessary, that accepts logs from different components. - * Once started, logs in this service are being setup to be forwarded to that logging server. + * Starts a logging server, if necessary, that accepts logs from different components. Once + * started, logs in this service are being setup to be forwarded to that logging server. * * @param logLevel maximal level of log events to be forwarded * @param logMasking true if masking of sensitive data should be applied to all log messages @@ -102,10 +102,12 @@ actualLogLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) } /** - * Initializes logging for this service using the URI of the dedicated logging server. - * If connecting to the logging server failed, or the optional address is missing, log events will be handled purely based on configuration packaged with this service. + * Initializes logging for this service using the URI of the dedicated logging server. If + * connecting to the logging server failed, or the optional address is missing, log events will be + * handled purely based on configuration packaged with this service. * - * @param logLevel optional maximal level of log events that will be handled by the logging infrastructure + * @param logLevel optional maximal level of log events that will be handled by the logging + * infrastructure * @param connectToExternalLogger optional address of the logging server * @param logMasking true if sensitive data should be masked in log events, false otherwise * @throws MissingConfigurationField if the config file has been mis-configured From d328f6b012da77741fb5c08a04ff405d172ed803 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Sun, 27 Aug 2023 15:40:57 +0200 Subject: [PATCH 23/31] User ServiceLoader to load implementation --- build.sbt | 7 ++-- .../org/enso/launcher/reflect-config.json | 38 ++++++++++++------- .../org/enso/launcher/resource-config.json | 1 - .../org/enso/runner/reflect-config.json | 25 ------------ .../org/enso/runner/resource-config.json | 3 -- .../org/enso/runner/serialization-config.json | 3 -- .../java/org/enso/logger/LoggerSetup.java | 21 ++-------- .../java/org/enso/logger/LogbackSetup.java | 1 + .../logging/LogbackLoggingServiceFactory.java | 1 + .../src/main/resources/enso-logging.conf | 2 - .../enso/logging/LoggingServiceFactory.java | 26 +++---------- .../enso/logging/LoggingServiceManager.java | 12 ++++-- .../enso/projectmanager/resource-config.json | 1 - 13 files changed, 46 insertions(+), 95 deletions(-) delete mode 100644 lib/scala/logging-logback/src/main/resources/enso-logging.conf diff --git a/build.sbt b/build.sbt index d04d37c6d0d3..d8cc4fd72ee1 100644 --- a/build.sbt +++ b/build.sbt @@ -735,10 +735,9 @@ lazy val `logging-logback` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion, - "com.typesafe" % "config" % typesafeConfigVersion, - "io.sentry" % "sentry-logback" % "6.28.0" % Provided, - akkaHttp + "org.slf4j" % "slf4j-api" % slf4jVersion, + "io.sentry" % "sentry-logback" % "6.28.0" % Provided, + "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided" ) ++ logbackPkg ) .dependsOn(`logging-config`) diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json index eec83d9bb71e..808a21f6cd3b 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json @@ -724,21 +724,31 @@ ] }, { - "name": "org.enso.logging.LogbackLoggingServiceFactory", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] + "name":"ch.qos.logback.classic.pattern.DateConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name": "org.enso.logger.LogbackSetup", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] + "name":"ch.qos.logback.classic.pattern.LevelConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.LoggerConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.classic.pattern.MessageConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, + { + "name":"ch.qos.logback.core.rolling.helper.IntegerTokenConverter", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json index df33089fd238..044086ab6f65 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json @@ -3,7 +3,6 @@ { "pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, { "pattern": "\\Qakka-http-version.conf\\E" }, { "pattern": "\\Qapplication.conf\\E" }, - { "pattern": "\\Qenso-logging.conf\\E" }, { "pattern": "\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, { "pattern": "\\Qreference.conf\\E" }, { "pattern": "\\Qversion.conf\\E" } diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index 459c46535994..5e3c589d9f28 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -220,31 +220,6 @@ "name": "org.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField", "methods": [{ "name": "", "parameterTypes": [] }] }, - { - "name": "org.enso.logging.LogbackLoggingServiceFactory", - "methods": [{ "name": "", "parameterTypes": [] } ] - }, - { "name": "org.enso.logger.LogbackSetup", - "methods": [{ "name": "get" }] - }, - { - "name": "org.enso.logging.LogbackLoggingServiceFactory", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "org.enso.logger.LogbackSetup", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json index a7b2190f3622..429aea7bd773 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/resource-config.json @@ -28,9 +28,6 @@ { "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, - { - "pattern":"\\Qenso-logging.conf\\E" - }, { "pattern":"\\Qapplication.conf\\E" }, diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/serialization-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/serialization-config.json index cafbbaf9d5ea..fbf84f6c509a 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/serialization-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/serialization-config.json @@ -278,9 +278,6 @@ { "name":"org.enso.compiler.pass.resolve.TypeSignatures$Signature" }, - { - "name":"org.enso.data.Shifted" - }, { "name":"org.enso.pkg.QualifiedName" }, diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index 2cd5fe5682f1..a1ddb7e0d5e5 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -1,8 +1,7 @@ package org.enso.logger; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import java.nio.file.Path; +import java.util.ServiceLoader; import org.enso.logger.config.LoggingServiceConfig; import org.enso.logger.config.MissingConfigurationField; import org.slf4j.event.Level; @@ -10,6 +9,8 @@ /** Base class to be implemented by the underlying logging implementation. */ public abstract class LoggerSetup { + private static final ServiceLoader loader = + ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); private static LoggerSetup _instance = null; private static Object lock = new Object(); @@ -17,21 +18,7 @@ public static LoggerSetup get() { if (_instance == null) { synchronized (lock) { if (_instance == null) { - - Config c = ConfigFactory.load("enso-logging"); - if (c.hasPath(implClassKey)) { - try { - String clazzName = c.getString(implClassKey); - Class clazz = Class.forName(clazzName); - _instance = (LoggerSetup) clazz.getConstructor().newInstance(); - } catch (Throwable e) { - e.printStackTrace(); - System.err.println( - "Failed to initialize LoggerSetup configuration class: " + e.getMessage()); - } - } else { - System.err.println("Missing log configuration class key:" + implClassKey); - } + _instance = loader.findFirst().get(); } } } diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java index b625340af6ad..365b0231944a 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.slf4j.event.Level; +@org.openide.util.lookup.ServiceProvider(service = LoggerSetup.class) public class LogbackSetup extends LoggerSetup { private LogbackSetup(LoggingServiceConfig config, LoggerContext context) { this.config = config; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java b/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java index b99c3adf988f..495031a6151f 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java +++ b/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java @@ -2,6 +2,7 @@ import java.net.URI; +@org.openide.util.lookup.ServiceProvider(service = LoggingServiceFactory.class) public class LogbackLoggingServiceFactory extends LoggingServiceFactory { @Override diff --git a/lib/scala/logging-logback/src/main/resources/enso-logging.conf b/lib/scala/logging-logback/src/main/resources/enso-logging.conf deleted file mode 100644 index 1b08165b7356..000000000000 --- a/lib/scala/logging-logback/src/main/resources/enso-logging.conf +++ /dev/null @@ -1,2 +0,0 @@ -org.enso.logging.LoggingServiceFactory.impl.class=org.enso.logging.LogbackLoggingServiceFactory -org.enso.logger.LoggerSetup.impl.class=org.enso.logger.LogbackSetup diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java index 85c844af134c..6b09187eccd0 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java @@ -1,38 +1,22 @@ package org.enso.logging; -import com.typesafe.config.Config; -import com.typesafe.config.ConfigFactory; import java.net.URI; +import java.util.ServiceLoader; public abstract class LoggingServiceFactory { + private static final ServiceLoader loader = + ServiceLoader.load(LoggingServiceFactory.class, LoggingServiceFactory.class.getClassLoader()); + public abstract LoggingService localServerFor(int port); @SuppressWarnings("unchecked") public static LoggingServiceFactory get() { if (_loggingServiceFactory == null) { - Config c = ConfigFactory.load("enso-logging.conf"); - if (c.hasPath(implClassKey)) { - try { - String clazzName = c.getString(implClassKey); - Class clazz = Class.forName(clazzName); - _loggingServiceFactory = - (LoggingServiceFactory) clazz.getDeclaredConstructor().newInstance(); - } catch (Throwable e) { - e.printStackTrace(); - System.err.println("Failed to initialize LoggingServiceFactory configuration class"); - return null; - } - - } else { - System.err.println("Missing log configuration class key: " + implClassKey); - return null; - } + _loggingServiceFactory = loader.findFirst().get(); } return _loggingServiceFactory; } private static LoggingServiceFactory _loggingServiceFactory = null; - - private static final String implClassKey = LoggingServiceFactory.class.getName() + ".impl.class"; } diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java index 66e294e344c5..13bf884c2311 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceManager.java @@ -28,10 +28,14 @@ public static Future setupServer( } else { if (config.appenders().containsKey(config.appender())) { currentLevel = logLevel; - var server = LoggingServiceFactory.get().localServerFor(port); - loggingService = server; - Appender appender = config.appenders().get(config.appender()); - return Future.apply(() -> server.start(logLevel, logPath, logFileSuffix, appender), ec); + return Future.apply( + () -> { + var server = LoggingServiceFactory.get().localServerFor(port); + loggingService = server; + Appender appender = config.appenders().get(config.appender()); + return server.start(logLevel, logPath, logFileSuffix, appender); + }, + ec); } else { throw new LoggerInitializationFailed(); } diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json index 05d5b61b4fc9..c25e59bb8678 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json @@ -3,7 +3,6 @@ { "pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, { "pattern": "\\Qakka-http-version.conf\\E" }, { "pattern": "\\Qapplication.conf\\E" }, - { "pattern": "\\Qenso-logging.conf\\E" }, { "pattern": "\\Qorg/enso/projectmanager/boot/ProjectManager$.class\\E" }, { "pattern": "\\Qorg/enso/projectmanager/control/core/CovariantFlatMapOps.class\\E" From d97d39c443e77e5c163af331a2e4680d9ff0b365 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Sun, 27 Aug 2023 23:38:48 +0200 Subject: [PATCH 24/31] nits --- .../enso/languageserver/boot/LanguageServerComponent.scala | 3 ++- .../main/scala/org/enso/languageserver/boot/MainModule.scala | 1 - .../scala/org/enso/languageserver/util/UnhandledLogging.scala | 2 -- .../scala/org/enso/launcher/components/LauncherRunner.scala | 1 - .../src/main/scala/org/enso/runner/LanguageServerApp.scala | 1 - .../src/main/scala/org/enso/runner/ProjectUploader.scala | 1 - .../src/main/java/org/enso/interpreter/epb/EpbContext.java | 4 ++-- .../runtime/src/main/scala/org/enso/compiler/Compiler.scala | 3 +-- .../enso/interpreter/runtime/DefaultPackageRepository.scala | 2 +- .../java/org/enso/interpreter/test/DebuggingEnsoTest.java | 2 +- .../enso/distribution/config/GlobalConfigurationManager.scala | 2 +- .../main/scala/org/enso/projectmanager/boot/MainModule.scala | 1 - .../scala/org/enso/projectmanager/boot/configuration.scala | 1 - .../scala/org/enso/projectmanager/util/UnhandledLogging.scala | 2 -- .../scala/org/enso/runtimeversionmanager/cli/Arguments.scala | 1 - .../scala/org/enso/runtimeversionmanager/runner/Runner.scala | 1 - 16 files changed, 8 insertions(+), 20 deletions(-) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala index 5b212f81507e..800056b2c4cd 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/boot/LanguageServerComponent.scala @@ -38,7 +38,8 @@ class LanguageServerComponent(config: LanguageServerConfig, logLevel: Level) override def start(): Future[ComponentStarted.type] = { logger.info("Starting Language Server...") val sampler = startSampling(config) - val module = new MainModule(config, logLevel) + logger.debug("Started [{}].", sampler.getClass.getName) + val module = new MainModule(config, logLevel) val bindJsonServer = for { binding <- module.jsonRpcServer.bind(config.interface, config.rpcPort) 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 58da2fc64b2b..2e12cfef1a95 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 @@ -297,7 +297,6 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { .option( RuntimeOptions.LOG_LEVEL, Converter.toJavaLevel(logLevel).getName - //JavaLoggingLogHandler.getJavaLogLevelFor(logLevel).getName ) .option(RuntimeOptions.LOG_MASKING, Masking.isMaskingEnabled.toString) .option(RuntimeOptions.EDITION_OVERRIDE, Info.currentEdition) diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala index 89a9e8cd290e..544e17f8c244 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala @@ -11,11 +11,9 @@ trait UnhandledLogging extends LazyLogging { this: Actor => private val akkaLogLevel = AkkaConverter .fromString(context.system.settings.LogLevel) .orElse(Level.ERROR) - //.getOrElse(LogLevel.Error) override def unhandled(message: Any): Unit = { if (Level.WARN.toInt <= akkaLogLevel.toInt) { - //if (implicitly[Ordering[]].lteq(LogLevel.Warning, akkaLogLevel)) { logger.warn("Received unknown message [{}].", message.getClass) } } diff --git a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala index ea5562a1563a..0c7115438a01 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/components/LauncherRunner.scala @@ -8,7 +8,6 @@ import org.enso.launcher.project.ProjectManager import org.enso.logger.masking.MaskedPath import java.net.URI -//import org.enso.loggingservice.LogLevel import org.enso.runtimeversionmanager.components.RuntimeVersionManager import org.enso.runtimeversionmanager.config.GlobalRunnerConfigurationManager import org.enso.runtimeversionmanager.runner._ diff --git a/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala b/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala index 1fcff089637a..adeada439688 100644 --- a/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala +++ b/engine/runner/src/main/scala/org/enso/runner/LanguageServerApp.scala @@ -5,7 +5,6 @@ import org.enso.languageserver.boot.{ LanguageServerConfig } import org.slf4j.event.Level -//import org.enso.loggingservice.LogLevel import java.util.concurrent.Semaphore diff --git a/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala b/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala index e30693ae67b7..11632bfdbac1 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ProjectUploader.scala @@ -5,7 +5,6 @@ import org.enso.cli.ProgressBar import org.enso.cli.task.{ProgressReporter, TaskProgress} import org.enso.languageserver.libraries.CompilerBasedDependencyExtractor import org.enso.libraryupload.{auth, LibraryUploader} -//import org.enso.loggingservice.LogLevel import org.enso.pkg.PackageManager import org.slf4j.event.Level diff --git a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java index 1a4bb073613c..4e40d4e476b3 100644 --- a/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java +++ b/engine/runtime-language-epb/src/main/java/org/enso/interpreter/epb/EpbContext.java @@ -73,13 +73,13 @@ private static void initializeLanguages( cdl.countDown(); try { for (var l : langs.split(",")) { - log.log(Level.FINEST, "Initializing language {}", l); + log.log(Level.FINEST, "Initializing language {0}", l); long then = System.currentTimeMillis(); var res = context.initializeInternal(null, l); long took = System.currentTimeMillis() - then; log.log( Level.FINE, - "Done initializing language {} with {} in {} ms", + "Done initializing language {0} with {1} in {2} ms", new Object[] {l, res, took}); } } finally { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index 0ddb2db39f12..e22970720951 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -200,8 +200,7 @@ class Compiler( context.log( Level.SEVERE, "Could not find entry point for compilation in package [{0}.{1}]", - pkg.namespace, - pkg.normalizedName + Array(pkg.namespace, pkg.normalizedName) ) CompletableFuture.completedFuture(false) case Some(m) => diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala index 6dabb9cac059..44031d4bdfd0 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/runtime/DefaultPackageRepository.scala @@ -52,7 +52,7 @@ private class DefaultPackageRepository( notificationHandler: NotificationHandler ) extends PackageRepository { - private lazy val logger = Logger[DefaultPackageRepository] + private val logger = Logger[DefaultPackageRepository] implicit private val fs: TruffleFileSystem = new TruffleFileSystem private val packageManager = new PackageManager[TruffleFile] diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java index 2bb59d0aee92..d2c38b342d01 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java @@ -59,7 +59,7 @@ public void initContext() { RuntimeOptions.LOG_LEVEL, "FINEST" ) - .logHandler(System.out)//OutputStream.nullOutputStream()) + .logHandler(OutputStream.nullOutputStream()) .build(); context = Context.newBuilder() diff --git a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala index 54915ca22d16..dcff3dec66a8 100644 --- a/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala +++ b/lib/scala/distribution-manager/src/main/scala/org/enso/distribution/config/GlobalConfigurationManager.scala @@ -12,7 +12,7 @@ import scala.util.{Failure, Success, Try, Using} /** Manages the global configuration of the distribution. */ class GlobalConfigurationManager(distributionManager: DistributionManager) { - private lazy val logger = Logger[GlobalConfigurationManager] + private val logger = Logger[GlobalConfigurationManager] /** Location of the global configuration file. */ def configLocation: Path = diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala index 34e0ae35c02a..b546742b3fe0 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/MainModule.scala @@ -5,7 +5,6 @@ import akka.stream.SystemMaterializer import cats.MonadError import org.enso.jsonrpc.JsonRpcServer import org.enso.logger.akka.AkkaConverter -//import org.enso.loggingservice.LogLevel import org.enso.projectmanager.boot.configuration.{ MainProcessConfig, ProjectManagerConfig diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala index a06344505ad3..647788d2ee40 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/boot/configuration.scala @@ -1,6 +1,5 @@ package org.enso.projectmanager.boot -//import org.enso.loggingservice.LogLevel import org.slf4j.event.Level import java.io.File diff --git a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala index 76ee6d0b1ea4..d48262a4ba04 100644 --- a/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala +++ b/lib/scala/project-manager/src/main/scala/org/enso/projectmanager/util/UnhandledLogging.scala @@ -3,7 +3,6 @@ package org.enso.projectmanager.util import akka.actor.Actor import com.typesafe.scalalogging.LazyLogging import org.enso.logger.akka.AkkaConverter -//import org.enso.loggingservice.LogLevel import org.slf4j.event.Level trait UnhandledLogging extends LazyLogging { this: Actor => @@ -13,7 +12,6 @@ trait UnhandledLogging extends LazyLogging { this: Actor => .orElse(Level.ERROR) override def unhandled(message: Any): Unit = { - //if (implicitly[Ordering[LogLevel]].lteq(LogLevel.Warning, akkaLogLevel)) { if (Level.WARN.toInt <= akkaLogLevel.toInt) { logger.warn("Received unknown message: {}", message.getClass) } diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala index 18038858705b..a20026002c1e 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/cli/Arguments.scala @@ -31,7 +31,6 @@ object Arguments { val provided = LoggerUtils.backwardCompatibleName(string.toLowerCase) Level .values() - //LogLevel.allLevels .find(_.toString.toLowerCase == provided) .toRight( OptsParseError(s"`$string` is not a valid log level.") diff --git a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala index d398089f7903..7c78b0d23a65 100644 --- a/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala +++ b/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/runner/Runner.scala @@ -9,7 +9,6 @@ import org.enso.logger.masking.MaskedString import org.slf4j.event.Level import java.net.URI -//import org.enso.loggingservice.LogLevel import org.enso.runtimeversionmanager.components.Manifest.JVMOptionsContext import org.enso.runtimeversionmanager.components.{ Engine, From d274e98b96bb97da273779928db89ed6952b306c Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 1 Sep 2023 11:16:20 +0200 Subject: [PATCH 25/31] Address PR comments --- build.sbt | 34 +++++++++---------- docs/infrastructure/logging.md | 17 ++++------ .../src/main/resources/application.conf | 2 +- .../enso/languageserver/boot/MainModule.scala | 2 +- .../CompilerBasedDependencyExtractor.scala | 2 +- .../util/UnhandledLogging.scala | 1 - .../src/main/resources/application.conf | 2 +- .../org/enso/runner/ContextFactory.scala | 2 +- .../java/org/enso/logger/LoggerSetup.java | 16 ++++----- .../java/org/enso/logger/config/Appender.java | 8 ++--- .../enso/logger/config/ConsoleAppender.java | 4 +-- .../org/enso/logger/config/FileAppender.java | 6 ++-- .../enso/logger/config/SentryAppender.java | 4 +-- .../enso/logger/config/SocketAppender.java | 6 ++-- .../main/java/org/enso/logger/Converter.java | 2 -- .../main/java/org/enso/logger/JulHandler.java | 14 ++++++-- .../org/enso/logger/ApplicationFilter.java | 0 .../java/org/enso/logger/LogbackSetup.java | 2 +- .../logging/LogbackLoggingServiceFactory.java | 0 .../java/org/enso/logging/LoggingServer.java | 0 .../enso/logging/LoggingServiceFactory.java | 15 ++++---- .../org/enso/logging/LoggingSetupHelper.java | 12 +++---- .../org/enso/logger/akka/AkkaConverter.java | 19 +++++++---- .../enso/projectmanager/reflect-config.json | 18 ---------- .../src/main/resources/application.conf | 6 ++-- .../projectmanager/boot/ProjectManager.scala | 2 +- 26 files changed, 93 insertions(+), 103 deletions(-) rename lib/scala/{logging-logback => logging-service-logback}/src/main/java/org/enso/logger/ApplicationFilter.java (100%) rename lib/scala/{logging-logback => logging-service-logback}/src/main/java/org/enso/logger/LogbackSetup.java (99%) rename lib/scala/{logging-logback => logging-service-logback}/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java (100%) rename lib/scala/{logging-logback => logging-service-logback}/src/main/java/org/enso/logging/LoggingServer.java (100%) diff --git a/build.sbt b/build.sbt index d8cc4fd72ee1..3c526b379a0d 100644 --- a/build.sbt +++ b/build.sbt @@ -268,8 +268,8 @@ lazy val enso = (project in file(".")) `logging-utils`, `logging-jutil`, `logging-config`, - `logging-logback`, `logging-service`, + `logging-service-logback`, `logging-utils-akka`, filewatcher, `logging-truffle-connector`, @@ -361,7 +361,7 @@ val akkaSLF4J = akkaPkg("slf4j") val akkaTestkitTyped = akkaPkg("actor-testkit-typed") % Test val akkaHttp = akkaHTTPPkg("http") val akkaSpray = akkaHTTPPkg("http-spray-json") -val akkaTest = logbackPkg.map(_ % Test) +val logbackTest = logbackPkg.map(_ % Test) val akka = Seq( akkaActor, @@ -685,8 +685,8 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % scalatestVersion % Test - ) ++ logbackPkg.map(_ % Test) + "org.scalatest" %% "scalatest" % scalatestVersion % Test + ) ++ logbackTest ) lazy val `logging-service` = project @@ -728,8 +728,8 @@ lazy val `logging-config` = project ) ) -lazy val `logging-logback` = project - .in(file("lib/scala/logging-logback")) +lazy val `logging-service-logback` = project + .in(file("lib/scala/logging-service-logback")) .configs(Test) .settings( frgaalJavaCompilerSetting, @@ -762,10 +762,10 @@ lazy val filewatcher = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "io.methvin" % "directory-watcher" % directoryWatcherVersion, - "commons-io" % "commons-io" % commonsIoVersion, - "org.scalatest" %% "scalatest" % scalatestVersion % Test - ) ++ logbackPkg.map(_ % Test) + "io.methvin" % "directory-watcher" % directoryWatcherVersion, + "commons-io" % "commons-io" % commonsIoVersion, + "org.scalatest" %% "scalatest" % scalatestVersion % Test + ) ++ logbackTest ) .dependsOn(testkit % Test) @@ -927,7 +927,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) .dependsOn(`logging-service`) .dependsOn(pkg) .dependsOn(`json-rpc-server`) - .dependsOn(`logging-logback` % Runtime) + .dependsOn(`logging-service-logback` % Runtime) .dependsOn(`json-rpc-server-test` % Test) .dependsOn(testkit % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -958,7 +958,7 @@ lazy val `json-rpc-server` = project .in(file("lib/scala/json-rpc-server")) .settings( frgaalJavaCompilerSetting, - libraryDependencies ++= akka ++ akkaTest, + libraryDependencies ++= akka ++ logbackTest, libraryDependencies ++= circe, libraryDependencies ++= Seq( "io.circe" %% "circe-literal" % circeVersion, @@ -1002,7 +1002,7 @@ lazy val searcher = project "com.typesafe.slick" %% "slick" % slickVersion, "org.xerial" % "sqlite-jdbc" % sqliteVersion, "org.scalatest" %% "scalatest" % scalatestVersion % Test - ) ++ logbackPkg.map(_ % Test) + ) ++ logbackTest ) .configs(Benchmark) .settings( @@ -1195,7 +1195,7 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`profiling-utils`) .dependsOn(filewatcher) .dependsOn(testkit % Test) - .dependsOn(`logging-logback` % Test) + .dependsOn(`logging-service-logback` % Test) .dependsOn(`library-manager-test` % Test) .dependsOn(`runtime-version-manager-test` % Test) @@ -1747,7 +1747,7 @@ lazy val `engine-runner` = project .dependsOn(`logging-jutil`) .dependsOn(`edition-updater`) .dependsOn(`logging-service`) - .dependsOn(`logging-logback` % Runtime) + .dependsOn(`logging-service-logback` % Runtime) .dependsOn(`polyglot-api`) lazy val launcher = project @@ -1815,8 +1815,8 @@ lazy val launcher = project .dependsOn(pkg) .dependsOn(`logging-utils` % "test->test") .dependsOn(`logging-service`) - .dependsOn(`logging-logback` % Test) - .dependsOn(`logging-logback` % Runtime) + .dependsOn(`logging-service-logback` % Test) + .dependsOn(`logging-service-logback` % Runtime) .dependsOn(`distribution-manager` % Test) .dependsOn(`runtime-version-manager-test` % Test) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index f08b808b18c6..604ed2776d64 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -48,11 +48,9 @@ The configuration has two main sections: - [applications' appenders](#appenders) (also known as configuration of log events output target) -During component's setup, its `application.conf` file is read, parsed and -verified and available as part of the class hierarchy defined in -`org.enso.logger.config` package. Class -`org.enso.logger.config.LoggingServiceConfig` encapsulates the complete -information represented by the `logging-service` key of the config file and is +During component's setup, its `application.conf` config file is parsed. The config's keys and values are validated and, if correct, +the parsed representation is available as an instance of `org.enso.logger.config.LoggingServiceConfig` class. The class encapsulates +the `logging-service` section of `application.conf` file and is used to programmatically initialize loggers. As per [configuration schema](https://github.com/lightbend/config) any key can @@ -133,7 +131,7 @@ Currently supported are The appenders are defined by the `logging-service.appenders`. Currently only a single appender can be selected at a time. The selection may also be done via an -environmental variable `$ENSO_DEFAULT_APPENDER`. +environmental variable `$ENSO_APPENDER_DEFAULT`. #### Format @@ -203,7 +201,7 @@ The two fields can be overridden via environment variables: } ``` -Sentry's Appender has a single required field, `dsn`. +Sentry's Appender has a single required field, `dsn`. The `dsn` value can be provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. ## JVM Architecture @@ -241,9 +239,8 @@ public class Foo { The `org.slf4j.Logger` instances have to know where to send log events. This setting is typically performed once, when the service starts, and applies globally during its execution. Currently, it is not possible to dynamically -change where log events are being stored. The main class used for setting up -logging is `org.enso.logger.LoggerSetup` but it should not be instantiated -directly by the users. Instead, it should be retrieved with the Thread-safe +change where log events are being stored. The main (abstract) class used for setting up +logging is `org.enso.logger.LoggerSetup`. An instance of that class can be retrieved with the thread-safe `org.enso.logger.LoggerSetup.get` factory method. `org.enso.logger.LoggerSetup` provides a number of `setupXYZAppender` methods that will direct loggers to send log events to an `XYZ` appender. Setting a specific hard-coded appender diff --git a/engine/language-server/src/main/resources/application.conf b/engine/language-server/src/main/resources/application.conf index 73876699e58a..247cb24bbae2 100644 --- a/engine/language-server/src/main/resources/application.conf +++ b/engine/language-server/src/main/resources/application.conf @@ -42,5 +42,5 @@ logging-service { } ] default-appender = socket - default-appender = ${?ENSO_DEFAULT_APPENDER} + default-appender = ${?ENSO_APPENDER_DEFAULT} } 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 2e12cfef1a95..504a5a3591e0 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 @@ -308,7 +308,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) { .out(stdOut) .err(stdErr) .in(stdIn) - .logHandler(new JulHandler()) + .logHandler(JulHandler.get()) .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/libraries/CompilerBasedDependencyExtractor.scala b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala index ef750b1e91d3..077cae40d869 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/libraries/CompilerBasedDependencyExtractor.scala @@ -65,7 +65,7 @@ class CompilerBasedDependencyExtractor(logLevel: Level) RuntimeOptions.LOG_LEVEL, Converter.toJavaLevel(logLevel).getName ) - .logHandler(new JulHandler()) + .logHandler(JulHandler.get()) .build new PolyglotContext(context) } diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala index 544e17f8c244..3e70882a0c22 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/util/UnhandledLogging.scala @@ -3,7 +3,6 @@ package org.enso.languageserver.util import akka.actor.Actor import com.typesafe.scalalogging.LazyLogging import org.enso.logger.akka.AkkaConverter -//import org.enso.loggingservice.LogLevel import org.slf4j.event.Level trait UnhandledLogging extends LazyLogging { this: Actor => diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index d569e2d9e322..9e79132d6e3e 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -31,5 +31,5 @@ logging-service { } ] default-appender = file - default-appender = ${?ENSO_DEFAULT_APPENDER} + default-appender = ${?ENSO_APPENDER_DEFAULT} } diff --git a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala index 4b7ed8b1a9a3..3268b7e69e78 100644 --- a/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala +++ b/engine/runner/src/main/scala/org/enso/runner/ContextFactory.scala @@ -88,7 +88,7 @@ class ContextFactory { RuntimeOptions.LOG_LEVEL, logLevelName ) - .logHandler(new JulHandler()) + .logHandler(JulHandler.get()) .build new PolyglotContext(context) } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index a1ddb7e0d5e5..aaf987e28ec4 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -11,17 +11,15 @@ public abstract class LoggerSetup { private static final ServiceLoader loader = ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); - private static LoggerSetup _instance = null; - private static Object lock = new Object(); + private static LoggerSetup _instance; + + static { + ServiceLoader loader = + ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); + _instance = loader.findFirst().get(); + } public static LoggerSetup get() { - if (_instance == null) { - synchronized (lock) { - if (_instance == null) { - _instance = loader.findFirst().get(); - } - } - } return _instance; } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java index 5446e6557af8..df66888ed25f 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/Appender.java @@ -9,7 +9,7 @@ * Base class for all appenders supported by Enso's logging configuration. Appenders determine what * to do with the recorded log events */ -public abstract class Appender { +public sealed abstract class Appender permits FileAppender, SocketAppender, SentryAppender, ConsoleAppender { /** * Returns the name of the appender @@ -50,7 +50,7 @@ public static Appender parse(Config config) throws MissingConfigurationField { * @param loggerSetup logger's setup to be used to be invoked with this appender * @return true if logger has been setup correctly using this configuration, false otherwise */ - public Boolean setup(Level logLevel, LoggerSetup loggerSetup) { + public boolean setup(Level logLevel, LoggerSetup loggerSetup) { return false; } @@ -61,12 +61,12 @@ public Boolean setup(Level logLevel, LoggerSetup loggerSetup) { * @param loggerSetup logger's setup to be used to be invoked with this appender * @return true if logger has been setup correctly using this configuration, false otherwise */ - public Boolean setupForPath( + public boolean setupForPath( Level logLevel, Path logRoot, String logPrefix, LoggerSetup loggerSetup) { return setup(logLevel, loggerSetup); } - public Boolean setupForURI(Level logLevel, String hostname, int port, LoggerSetup loggerSetup) { + public boolean setupForURI(Level logLevel, String hostname, int port, LoggerSetup loggerSetup) { return setup(logLevel, loggerSetup); } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java index 52d86ea2c511..c6966f6507c5 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/ConsoleAppender.java @@ -5,7 +5,7 @@ import org.slf4j.event.Level; /** Config for log configuration that appends to the console */ -public class ConsoleAppender extends Appender { +public final class ConsoleAppender extends Appender { private final String pattern; @@ -20,7 +20,7 @@ public static ConsoleAppender parse(Config config) { } @Override - public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + public boolean setup(Level logLevel, LoggerSetup appenderSetup) { return appenderSetup.setupConsoleAppender(logLevel); } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java index b7cbf69e75fb..c1fe2a70098c 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/FileAppender.java @@ -7,7 +7,7 @@ import org.slf4j.event.Level; /** Config for log configuration that appends to the file. */ -public class FileAppender extends Appender { +public final class FileAppender extends Appender { private final boolean append; private final boolean immediateFlush; private final String pattern; @@ -65,13 +65,13 @@ public static Appender parse(Config config) { } @Override - public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + public boolean setup(Level logLevel, LoggerSetup appenderSetup) { return appenderSetup.setupFileAppender( logLevel, logLocation.logRoot(), logLocation.logPrefix()); } @Override - public Boolean setupForPath( + public boolean setupForPath( Level logLevel, Path componentLogPath, String componentLogPrefix, LoggerSetup loggerSetup) { return loggerSetup.setupFileAppender(logLevel, componentLogPath, componentLogPrefix); } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java index 99d660377804..648721b87b2d 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -5,7 +5,7 @@ import org.slf4j.event.Level; /** Config for log configuration that sends logs to sentry.io service. */ -public class SentryAppender extends Appender { +public final class SentryAppender extends Appender { public String getDsn() { return dsn; @@ -23,7 +23,7 @@ public static Appender parse(Config config) throws MissingConfigurationField { } @Override - public Boolean setup(Level logLevel, LoggerSetup appenderSetup) { + public boolean setup(Level logLevel, LoggerSetup appenderSetup) { return appenderSetup.setupSentryAppender(logLevel); } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java index 2731c37eb47f..372c9ca2d585 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SocketAppender.java @@ -5,7 +5,7 @@ import org.slf4j.event.Level; /** Config for log configuration that forwards logs to the network socket as-is. */ -public class SocketAppender extends Appender { +public final class SocketAppender extends Appender { private String name; private String host; @@ -53,12 +53,12 @@ public static Appender parse(Config config) throws MissingConfigurationField { } @Override - public Boolean setup(Level logLevel, LoggerSetup loggerSetup) { + public boolean setup(Level logLevel, LoggerSetup loggerSetup) { return loggerSetup.setupSocketAppender(logLevel, host, port); } @Override - public Boolean setupForURI(Level logLevel, String host, int port, LoggerSetup loggerSetup) { + public boolean setupForURI(Level logLevel, String host, int port, LoggerSetup loggerSetup) { return loggerSetup.setupSocketAppender(logLevel, host, port); } diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java index 24e003894d2d..ad0f4a74670b 100644 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java @@ -6,8 +6,6 @@ public class Converter { - public static Level defaultLogLevel = TRACE; - /** Determines what is the smallest Java level that is still debug and not trace. */ private static int defaultLevelDebugCutOff = Math.min(java.util.logging.Level.FINE.intValue(), java.util.logging.Level.CONFIG.intValue()); diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java index 0ae4623dcbdb..ea6a8550c8e4 100644 --- a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java +++ b/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java @@ -10,14 +10,24 @@ import org.slf4j.LoggerFactory; /** java.util.logging.Handler that propagates all events to the equivalent SLF4J implementation. */ -public class JulHandler extends Handler { +public final class JulHandler extends Handler { + + private static final Handler _handler; + + static { + _handler = new JulHandler(); + } private Formatter formattter; - public JulHandler() { + private JulHandler() { this.formattter = new SimpleFormatter(); } + public static final Handler get() { + return _handler; + } + @Override public void publish(LogRecord record) { Logger logger = LoggerFactory.getLogger(record.getLoggerName()); diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/ApplicationFilter.java similarity index 100% rename from lib/scala/logging-logback/src/main/java/org/enso/logger/ApplicationFilter.java rename to lib/scala/logging-service-logback/src/main/java/org/enso/logger/ApplicationFilter.java diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java similarity index 99% rename from lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java rename to lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java index 365b0231944a..aed46e88f98c 100644 --- a/lib/scala/logging-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -27,7 +27,7 @@ import org.slf4j.event.Level; @org.openide.util.lookup.ServiceProvider(service = LoggerSetup.class) -public class LogbackSetup extends LoggerSetup { +public final class LogbackSetup extends LoggerSetup { private LogbackSetup(LoggingServiceConfig config, LoggerContext context) { this.config = config; this.context = context; diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java similarity index 100% rename from lib/scala/logging-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java rename to lib/scala/logging-service-logback/src/main/java/org/enso/logging/LogbackLoggingServiceFactory.java diff --git a/lib/scala/logging-logback/src/main/java/org/enso/logging/LoggingServer.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java similarity index 100% rename from lib/scala/logging-logback/src/main/java/org/enso/logging/LoggingServer.java rename to lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java index 6b09187eccd0..50ff3efa08fc 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java @@ -5,18 +5,19 @@ public abstract class LoggingServiceFactory { - private static final ServiceLoader loader = - ServiceLoader.load(LoggingServiceFactory.class, LoggingServiceFactory.class.getClassLoader()); + private static LoggingServiceFactory _loggingServiceFactory; + + static { + ServiceLoader loader = + ServiceLoader.load( + LoggingServiceFactory.class, LoggingServiceFactory.class.getClassLoader()); + _loggingServiceFactory = loader.findFirst().get(); + } public abstract LoggingService localServerFor(int port); @SuppressWarnings("unchecked") public static LoggingServiceFactory get() { - if (_loggingServiceFactory == null) { - _loggingServiceFactory = loader.findFirst().get(); - } return _loggingServiceFactory; } - - private static LoggingServiceFactory _loggingServiceFactory = null; } diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java index 4f0058b3ea18..4b281983825a 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java @@ -62,24 +62,23 @@ public void setupFallback() { * @param logLevel maximal level of log events to be forwarded * @param logMasking true if masking of sensitive data should be applied to all log messages */ - public void setupServerAndForwardLogs(Option logLevel, boolean logMasking) + public void setupServerAndForwardLogs(Level logLevel, boolean logMasking) throws MissingConfigurationField { var loggerSetup = LoggerSetup.get(); var config = loggerSetup.getConfig(); if (config.loggingServerNeedsBoot()) { int actualPort = config.getServer().port(); - Level actualLogLevel = logLevel.getOrElse(() -> defaultLogLevel()); LoggingServiceManager.setupServer( - actualLogLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) + logLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) .onComplete( (result) -> { try { if (result.isFailure()) { - setup(logLevel, Option.empty(), logMasking, loggerSetup); + setup(Option.apply(logLevel), Option.empty(), logMasking, loggerSetup); } else { URI uri = result.get(); Masking.setup(logMasking); - if (!loggerSetup.setup(actualLogLevel)) { + if (!loggerSetup.setup(logLevel)) { LoggingServiceManager.teardown(); loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()); } else { @@ -94,8 +93,7 @@ actualLogLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) ec); } else { // Setup logger according to config - var actualLogLevel = logLevel.getOrElse(() -> defaultLogLevel()); - if (loggerSetup.setup(actualLogLevel)) { + if (loggerSetup.setup(logLevel)) { loggingServiceEndpointPromise.success(Option.empty()); } } diff --git a/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java b/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java index 36ca444694a7..d262fb2e2c45 100644 --- a/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java +++ b/lib/scala/logging-utils-akka/src/main/java/org/enso/logger/akka/AkkaConverter.java @@ -14,20 +14,27 @@ public class AkkaConverter { * @return an equivalnet of `level` in Akka's LogLevel */ public static akka.event.Logging.LogLevel toAkka(Level level) { + int lvl; switch (level) { case ERROR: - return new akka.event.Logging.LogLevel(1); // Error + lvl = akka.event.Logging$.MODULE$.ErrorLevel(); + break; case WARN: - return new akka.event.Logging.LogLevel(2); // Warning + lvl = akka.event.Logging$.MODULE$.WarningLevel(); + break; case INFO: - return new akka.event.Logging.LogLevel(3); // Info + lvl = akka.event.Logging$.MODULE$.InfoLevel(); + break; case DEBUG: - return new akka.event.Logging.LogLevel(4); // Debug + lvl = akka.event.Logging$.MODULE$.DebugLevel(); + break; case TRACE: - return new akka.event.Logging.LogLevel(4); // Debug + lvl = akka.event.Logging$.MODULE$.DebugLevel(); + break; default: - return new akka.event.Logging.LogLevel(1); + lvl = akka.event.Logging$.MODULE$.ErrorLevel(); } + return new akka.event.Logging.LogLevel(lvl); } /** diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json index ed8a50abc589..34838505d336 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json @@ -566,24 +566,6 @@ } ] }, - { - "name": "org.enso.logging.LogbackLoggingServiceFactory", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "org.enso.logger.LogbackSetup", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index 3917248b8907..ec763544b77b 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -37,7 +37,7 @@ logging-service { name = "console" } ] - default-appender = ${?ENSO_DEFAULT_APPENDER} + default-appender = ${?ENSO_APPENDER_DEFAULT} default-appender = socket server { start = true @@ -55,8 +55,8 @@ logging-service { }, { name = "sentry" - dsn = ${?ENSO_SENTRY_APPENDER_DSN} - dsn = "" + dsn = "" + dsn = ${?ENSO_APPENDER_SENTRY_DSN} }, { name = "console" 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 b4741e73a87f..4d733f11caf8 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 @@ -270,7 +270,7 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ZIO .attempt { Logging.initLogger() - Logging.setupServerAndForwardLogs(Some(level), logMasking) + Logging.setupServerAndForwardLogs(level, logMasking) () } .catchAll { exception => From 599eeabc873e16979d6ab5d612bc2e0d35d5fcc5 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 1 Sep 2023 11:18:15 +0200 Subject: [PATCH 26/31] linter --- docs/infrastructure/logging.md | 30 +++++++++++-------- .../src/main/resources/application.conf | 4 +-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index 604ed2776d64..fb147b90b4ec 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -48,10 +48,12 @@ The configuration has two main sections: - [applications' appenders](#appenders) (also known as configuration of log events output target) -During component's setup, its `application.conf` config file is parsed. The config's keys and values are validated and, if correct, -the parsed representation is available as an instance of `org.enso.logger.config.LoggingServiceConfig` class. The class encapsulates -the `logging-service` section of `application.conf` file and is -used to programmatically initialize loggers. +During component's setup, its `application.conf` config file is parsed. The +config's keys and values are validated and, if correct, the parsed +representation is available as an instance of +`org.enso.logger.config.LoggingServiceConfig` class. The class encapsulates the +`logging-service` section of `application.conf` file and is used to +programmatically initialize loggers. As per [configuration schema](https://github.com/lightbend/config) any key can have a default value that can be overridden by an environmental variable. For @@ -201,7 +203,8 @@ The two fields can be overridden via environment variables: } ``` -Sentry's Appender has a single required field, `dsn`. The `dsn` value can be provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. +Sentry's Appender has a single required field, `dsn`. The `dsn` value can be +provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. ## JVM Architecture @@ -239,14 +242,15 @@ public class Foo { The `org.slf4j.Logger` instances have to know where to send log events. This setting is typically performed once, when the service starts, and applies globally during its execution. Currently, it is not possible to dynamically -change where log events are being stored. The main (abstract) class used for setting up -logging is `org.enso.logger.LoggerSetup`. An instance of that class can be retrieved with the thread-safe -`org.enso.logger.LoggerSetup.get` factory method. `org.enso.logger.LoggerSetup` -provides a number of `setupXYZAppender` methods that will direct loggers to send -log events to an `XYZ` appender. Setting a specific hard-coded appender -programmatically should however be avoided by the users. Instead, one should -invoke one of the overloaded `setup` variants that initialize loggers based on -the provided `logging-service` configuration. +change where log events are being stored. The main (abstract) class used for +setting up logging is `org.enso.logger.LoggerSetup`. An instance of that class +can be retrieved with the thread-safe `org.enso.logger.LoggerSetup.get` factory +method. `org.enso.logger.LoggerSetup` provides a number of `setupXYZAppender` +methods that will direct loggers to send log events to an `XYZ` appender. +Setting a specific hard-coded appender programmatically should however be +avoided by the users. Instead, one should invoke one of the overloaded `setup` +variants that initialize loggers based on the provided `logging-service` +configuration. ```java package foo; diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index ec763544b77b..ac71f1ea6633 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -44,9 +44,9 @@ logging-service { start = ${?ENSO_LOGSERVER_START} port = 6000 port = ${?ENSO_LOGSERVER_PORT} - appenders = [ + appenders = [ # file/console/socket/sentry { - name = "file" # file/console/socket/sentry + name = "file" rolling-policy { max-file-size = "100MB" max-history = 30 From 48453503af25df4660233948d67a06aa7ac4b539 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 1 Sep 2023 11:21:35 +0200 Subject: [PATCH 27/31] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jaroslav Tulach Co-authored-by: Radosław Waśko --- docs/infrastructure/logging.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index fb147b90b4ec..808c3220bce5 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -12,8 +12,8 @@ The Enso project features a centralised logging service to allow for the aggregation of logs from multiple components. This service can be started with one of the main components, allowing other components to connect to it. The service aggregates all logs in one place for easier analysis of the interaction -between components. Components can also write to console, files directly without -involving an additional logging server. +between components. Components can also log to console or files directly without +involving the centralized logging service. @@ -56,7 +56,7 @@ representation is available as an instance of programmatically initialize loggers. As per [configuration schema](https://github.com/lightbend/config) any key can -have a default value that can be overridden by an environmental variable. For +have a default value that can be overridden by an environment variable. For example ``` @@ -191,7 +191,7 @@ Configuration The two fields can be overridden via environment variables: -- `hostanme` has an equivalent `$ENSO_LOGSERVER_HOSTNAME` variable +- `hostname` has an equivalent `$ENSO_LOGSERVER_HOSTNAME` variable - `port` has an equivalent `$ENSO_LOGSERVER_PORT` variable #### Sentry Appender From 69ca6661b31f2197ffdc3e5f61b24306fcee7a50 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 1 Sep 2023 16:47:50 +0200 Subject: [PATCH 28/31] Addressing more PR comments --- build.sbt | 17 ++--------------- docs/infrastructure/logging.md | 18 ++++++++++++++---- .../org/enso/launcher/reflect-config.json | 4 ++++ .../src/main/resources/application.conf | 4 ++-- .../launcher/cli/LauncherApplication.scala | 2 -- .../scala/org/enso/launcher/cli/Main.scala | 9 ++++----- .../org/enso/runner/reflect-config.json | 4 ++++ .../org/enso/logging/LoggingSetupHelper.java | 10 ++++++---- .../main/java/org/enso/logger/Converter.java | 0 .../main/java/org/enso/logger/JulHandler.java | 0 .../enso/projectmanager/reflect-config.json | 4 ++++ .../projectmanager/boot/ProjectManager.scala | 3 +-- 12 files changed, 41 insertions(+), 34 deletions(-) rename lib/scala/{logging-jutil => logging-utils}/src/main/java/org/enso/logger/Converter.java (100%) rename lib/scala/{logging-jutil => logging-utils}/src/main/java/org/enso/logger/JulHandler.java (100%) diff --git a/build.sbt b/build.sbt index 3c526b379a0d..e27f9b541d94 100644 --- a/build.sbt +++ b/build.sbt @@ -266,7 +266,6 @@ lazy val enso = (project in file(".")) `task-progress-notifications`, `profiling-utils`, `logging-utils`, - `logging-jutil`, `logging-config`, `logging-service`, `logging-service-logback`, @@ -685,7 +684,8 @@ lazy val `logging-utils` = project frgaalJavaCompilerSetting, version := "0.1", libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % scalatestVersion % Test + "org.scalatest" %% "scalatest" % scalatestVersion % Test, + "org.slf4j" % "slf4j-api" % slf4jVersion ) ++ logbackTest ) @@ -705,17 +705,6 @@ lazy val `logging-service` = project .dependsOn(`logging-utils`) .dependsOn(`logging-config`) -lazy val `logging-jutil` = project - .in(file("lib/scala/logging-jutil")) - .configs(Test) - .settings( - frgaalJavaCompilerSetting, - version := "0.1", - libraryDependencies ++= Seq( - "org.slf4j" % "slf4j-api" % slf4jVersion - ) - ) - lazy val `logging-config` = project .in(file("lib/scala/logging-config")) .configs(Test) @@ -1186,7 +1175,6 @@ lazy val `language-server` = (project in file("engine/language-server")) .dependsOn(`edition-updater`) .dependsOn(`logging-utils-akka`) .dependsOn(`logging-service`) - .dependsOn(`logging-jutil`) .dependsOn(`polyglot-api`) .dependsOn(`searcher`) .dependsOn(`text-buffer`) @@ -1744,7 +1732,6 @@ lazy val `engine-runner` = project .dependsOn(cli) .dependsOn(`library-manager`) .dependsOn(`language-server`) - .dependsOn(`logging-jutil`) .dependsOn(`edition-updater`) .dependsOn(`logging-service`) .dependsOn(`logging-service-logback` % Runtime) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index 808c3220bce5..40130bbbc286 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -157,6 +157,8 @@ message via `pattern` field e.g. #### File Appender +Enabled with `ENSO_APPENDER_DEFAULT=file` environment variable. + File appender directs all log events to a log file: ``` @@ -175,10 +177,13 @@ File appender directs all log events to a log file: Rolling policy is a fully optional property of File Appender that would trigger automatic log rotation. All properties are optional with some reasonable -defaults if not specified. +defaults if missing (defined in `org.enso.logger.config.FileAppender` config +class). #### Socket Appender +Enabled with `ENSO_APPENDER_DEFAULT=socket` environment variable. + Configuration ``` @@ -196,6 +201,8 @@ The two fields can be overridden via environment variables: #### Sentry Appender +Enabled with `ENSO_APPENDER_DEFAULT=sentry` environment variable. + ``` { name = "sentry" @@ -208,9 +215,12 @@ provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. ## JVM Architecture -Previously, Enso came with its own implementation of Logger. That has been -replaced with a mostly off-the-shelf logging implementation, avoiding -maintenance costs for features that were already present in them. +Enso's logging makes use of two logging APIs - `java.util.logging` and +`org.slf4j`. The former is being used Truffle runtime, which itself relies on +`jul`, while the latter is used everywhere else. The implementation of the +logging is using off the shelf `Logback` implementation with some custom setup +methods. The two APIss cooperate by essentially forwarding log messages from the +former to the latter. ### SLF4J Interface diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json index 808a21f6cd3b..73ca20dcc9e3 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json @@ -739,6 +739,10 @@ "name":"ch.qos.logback.classic.pattern.LoggerConverter", "methods":[{"name":"","parameterTypes":[] }] }, + { + "name":"ch.qos.logback.classic.pattern.NopThrowableInformationConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, { "name":"ch.qos.logback.classic.pattern.MessageConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/engine/launcher/src/main/resources/application.conf b/engine/launcher/src/main/resources/application.conf index 9e79132d6e3e..59f758c9e1a9 100644 --- a/engine/launcher/src/main/resources/application.conf +++ b/engine/launcher/src/main/resources/application.conf @@ -23,11 +23,11 @@ logging-service { }, { name = "file", - pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n" }, { name = "console" - pattern = "[%level{lowercase=true}] [%d{yyyy-MM-ddTHH:mm:ssXXX}] [%logger] %msg%n%nopex" + pattern = "[%level{lowercase=true}] [%d{yyyy-MM-dd'T'HH:mm:ssXXX}] [%logger] %msg%n%nopex" } ] default-appender = file diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala index 1c38d5f45cc6..5c1d4266f6b7 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/LauncherApplication.scala @@ -654,8 +654,6 @@ object LauncherApplication { Launcher.ensurePortable() } - LauncherLogging.initLogger() - val globalCLIOptions = cli.GlobalCLIOptions( autoConfirm = autoConfirm, hideProgress = hideProgress, diff --git a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala index 386a81d8d27c..cc9762a7af0f 100644 --- a/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala +++ b/engine/launcher/src/main/scala/org/enso/launcher/cli/Main.scala @@ -8,9 +8,6 @@ import org.enso.launcher.upgrade.LauncherUpgrader /** Defines the entry point for the launcher. */ object Main { - private def initLogger(): Unit = { - LauncherLogging.initLogger() - } private def runAppHandlingParseErrors(args: Array[String]): Int = LauncherApplication.application.run(args) match { @@ -27,7 +24,8 @@ object Main { /** Entry point of the application. */ def main(args: Array[String]): Unit = { - initLogger() + // Disable logging prior to parsing arguments (may generate additional and unnecessary logs) + LauncherLogging.initLogger() val exitCode = try { LauncherUpgrader.recoverUpgradeRequiredErrors(args) { @@ -35,7 +33,8 @@ object Main { } } catch { case e: Exception => - logger.error(s"A fatal error has occurred: $e", e) + LauncherLogging.setupFallback() + logger.error("A fatal error has occurred: {}", e.getMessage, e) 1 } diff --git a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json index 5e3c589d9f28..ab8d6a6b1b29 100644 --- a/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json +++ b/engine/runner/src/main/resources/META-INF/native-image/org/enso/runner/reflect-config.json @@ -232,6 +232,10 @@ "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", "methods":[{"name":"","parameterTypes":[] }] }, + { + "name":"ch.qos.logback.classic.pattern.NopThrowableInformationConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, { "name":"ch.qos.logback.classic.pattern.LoggerConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java index 4b281983825a..35380c816fbf 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingSetupHelper.java @@ -62,8 +62,8 @@ public void setupFallback() { * @param logLevel maximal level of log events to be forwarded * @param logMasking true if masking of sensitive data should be applied to all log messages */ - public void setupServerAndForwardLogs(Level logLevel, boolean logMasking) - throws MissingConfigurationField { + public void setup(Level logLevel, boolean logMasking) throws MissingConfigurationField { + initLogger(); var loggerSetup = LoggerSetup.get(); var config = loggerSetup.getConfig(); if (config.loggingServerNeedsBoot()) { @@ -112,6 +112,7 @@ logLevel, actualPort, logPath(), logFileSuffix(), config.getServer(), ec) */ public void setup(Option logLevel, Option connectToExternalLogger, boolean logMasking) throws MissingConfigurationField { + initLogger(); setup(logLevel, connectToExternalLogger, logMasking, LoggerSetup.get()); } @@ -128,7 +129,8 @@ private void setup( loggerSetup.setupSocketAppender(actualLogLevel, uri.getHost(), uri.getPort()); if (!initialized) { // Fallback - initialized = loggerSetup.setup(actualLogLevel); + initialized = + loggerSetup.setup(actualLogLevel, logPath(), logFileSuffix(), loggerSetup.getConfig()); if (!initialized) { // Fallback to console initialized = loggerSetup.setupConsoleAppender(actualLogLevel); @@ -141,7 +143,7 @@ private void setup( loggingServiceEndpointPromise.failure(new LoggerInitializationFailed()); } } else { - if (loggerSetup.setup(actualLogLevel)) { + if (loggerSetup.setup(actualLogLevel, logPath(), logFileSuffix(), loggerSetup.getConfig())) { Masking.setup(logMasking); loggingServiceEndpointPromise.success(Option.empty()); } else { diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java b/lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java similarity index 100% rename from lib/scala/logging-jutil/src/main/java/org/enso/logger/Converter.java rename to lib/scala/logging-utils/src/main/java/org/enso/logger/Converter.java diff --git a/lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java b/lib/scala/logging-utils/src/main/java/org/enso/logger/JulHandler.java similarity index 100% rename from lib/scala/logging-jutil/src/main/java/org/enso/logger/JulHandler.java rename to lib/scala/logging-utils/src/main/java/org/enso/logger/JulHandler.java diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json index 34838505d336..bb81ecc57744 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json @@ -582,6 +582,10 @@ "name":"ch.qos.logback.classic.pattern.LoggerConverter", "methods":[{"name":"","parameterTypes":[] }] }, + { + "name":"ch.qos.logback.classic.pattern.NopThrowableInformationConverter", + "methods":[{"name":"","parameterTypes":[] }] + }, { "name":"ch.qos.logback.classic.pattern.MessageConverter", "methods":[{"name":"","parameterTypes":[] }] 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 4d733f11caf8..9e8fa34416af 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 @@ -269,8 +269,7 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { } ZIO .attempt { - Logging.initLogger() - Logging.setupServerAndForwardLogs(level, logMasking) + Logging.setup(level, logMasking) () } .catchAll { exception => From 3ccf22d7574a676b7ea2ee40b53b97d7846f478c Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Sun, 3 Sep 2023 21:38:14 +0200 Subject: [PATCH 29/31] Some Sentry additions, tested with prod --- build.sbt | 7 +++- docs/infrastructure/logging.md | 16 +++++++- .../java/org/enso/logger/LoggerSetup.java | 6 +-- .../enso/logger/config/SentryAppender.java | 40 ++++++++++++++++--- .../java/org/enso/logger/LogbackSetup.java | 23 ++++++++++- .../java/org/enso/logging/LoggingServer.java | 3 +- .../projectmanager/native-image.properties | 2 +- .../src/main/resources/application.conf | 6 +-- 8 files changed, 85 insertions(+), 18 deletions(-) diff --git a/build.sbt b/build.sbt index e27f9b541d94..8917d2a98687 100644 --- a/build.sbt +++ b/build.sbt @@ -685,7 +685,7 @@ lazy val `logging-utils` = project version := "0.1", libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % scalatestVersion % Test, - "org.slf4j" % "slf4j-api" % slf4jVersion + "org.slf4j" % "slf4j-api" % slf4jVersion ) ++ logbackTest ) @@ -725,7 +725,7 @@ lazy val `logging-service-logback` = project version := "0.1", libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, - "io.sentry" % "sentry-logback" % "6.28.0" % Provided, + "io.sentry" % "sentry-logback" % "6.28.0", "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided" ) ++ logbackPkg ) @@ -890,6 +890,9 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) "zio.internal.ZScheduler$$anon$4", "zio.Runtime$", "zio.FiberRef$" + ), + additionalOptions = Seq( + "--trace-class-initialization=com.typesafe.config.impl.ConfigImpl$SystemPropertiesHolder,com.typesafe.config.impl.ConfigImpl$EnvVariablesHolder" ) ) .dependsOn(VerifyReflectionSetup.run) diff --git a/docs/infrastructure/logging.md b/docs/infrastructure/logging.md index 40130bbbc286..77c5291aeab6 100644 --- a/docs/infrastructure/logging.md +++ b/docs/infrastructure/logging.md @@ -207,11 +207,16 @@ Enabled with `ENSO_APPENDER_DEFAULT=sentry` environment variable. { name = "sentry" dsn = + flush-timeout = + debug = } ``` Sentry's Appender has a single required field, `dsn`. The `dsn` value can be -provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. +provided via an environment variable `ENSO_APPENDER_SENTRY_DSN`. `flush-timeout` +determines how often logger should send its collected events to sentry.io +service. If `debug` value is `true`, logging will print to stdout additional +trace information of the logging process itself. ## JVM Architecture @@ -222,6 +227,15 @@ logging is using off the shelf `Logback` implementation with some custom setup methods. The two APIss cooperate by essentially forwarding log messages from the former to the latter. +While typically any SLF4J customization would be performed via custom +`LoggerFacotry` and `Logger` implementation that is returned via a +`StaticLoggerBinder` instance, this is not possible for our use-case: + +- file logging requires Enso-specific directory which is only known during + runtime +- centralized logging +- modifying log levels without recompilation + ### SLF4J Interface The user code must not be calling any of the underlying implementations, such as diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index aaf987e28ec4..64bb34ee0e58 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -8,9 +8,6 @@ /** Base class to be implemented by the underlying logging implementation. */ public abstract class LoggerSetup { - - private static final ServiceLoader loader = - ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); private static LoggerSetup _instance; static { @@ -59,9 +56,10 @@ public static LoggerSetup get() { * sentry's dependency appropriate to the logging implementation. * * @param logLevel the maximal level of logs that will be displayed + * @param logRoot the root directory where logs are located * @return true if logger was setup correctly, false otherwise */ - public abstract boolean setupSentryAppender(Level logLevel); + public abstract boolean setupSentryAppender(Level logLevel, Path logRoot); /** * Sets up loggers so that all events are being discarded. diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java index 648721b87b2d..35fa6022aa9c 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/config/SentryAppender.java @@ -1,6 +1,7 @@ package org.enso.logger.config; import com.typesafe.config.Config; +import java.nio.file.Path; import org.enso.logger.LoggerSetup; import org.slf4j.event.Level; @@ -13,18 +14,43 @@ public String getDsn() { private String dsn; - private SentryAppender(String dsn) { + public Integer getFlushTimeoutMs() { + return flushTimeoutMs; + } + + private Integer flushTimeoutMs; + + public boolean isDebugEnabled() { + return debugEnabled; + } + + private boolean debugEnabled; + + private SentryAppender(String dsn, Integer flushTimeoutMs, boolean debugEnabled) { this.dsn = dsn; + this.flushTimeoutMs = flushTimeoutMs; + this.debugEnabled = debugEnabled; } public static Appender parse(Config config) throws MissingConfigurationField { - if (config.hasPath(dsnKey)) return new SentryAppender(config.getString(dsnKey)); - else throw new MissingConfigurationField(dsnKey); + if (config.hasPath(dsnKey)) { + String dsn = config.getString(dsnKey); + Integer flushTimeoutMs = + config.hasPath(flushTimeoutKey) ? Integer.valueOf(config.getInt(flushTimeoutKey)) : null; + boolean debugEnabled = config.hasPath(debugKey) ? config.getBoolean(debugKey) : false; + return new SentryAppender(dsn, flushTimeoutMs, debugEnabled); + } else throw new MissingConfigurationField(dsnKey); } @Override - public boolean setup(Level logLevel, LoggerSetup appenderSetup) { - return appenderSetup.setupSentryAppender(logLevel); + public boolean setup(Level logLevel, LoggerSetup loggerSetup) { + return loggerSetup.setupSentryAppender(logLevel, null); + } + + @Override + public boolean setupForPath( + Level logLevel, Path logRoot, String logPrefix, LoggerSetup loggerSetup) { + return loggerSetup.setupSentryAppender(logLevel, logRoot); } @Override @@ -33,5 +59,9 @@ public String getName() { } private static final String dsnKey = "dsn"; + + private static final String flushTimeoutKey = "flush-timeout"; + + private static final String debugKey = "debug"; public static final String appenderName = "sentry"; } diff --git a/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java index aed46e88f98c..858249b7db2a 100644 --- a/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -12,9 +12,13 @@ import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.util.Duration; import ch.qos.logback.core.util.FileSize; +import io.sentry.SentryLevel; import io.sentry.SentryOptions; +import io.sentry.SystemOutLogger; import io.sentry.logback.SentryAppender; import java.io.File; @@ -28,6 +32,7 @@ @org.openide.util.lookup.ServiceProvider(service = LoggerSetup.class) public final class LogbackSetup extends LoggerSetup { + private LogbackSetup(LoggingServiceConfig config, LoggerContext context) { this.config = config; this.context = context; @@ -202,7 +207,9 @@ public boolean setupConsoleAppender(Level logLevel) { } @Override - public boolean setupSentryAppender(Level logLevel) { + public boolean setupSentryAppender(Level logLevel, Path logRoot) { + // TODO: handle proxy + // TODO: shutdown timeout configuration try { LoggerAndContext env = contextInit(logLevel, config); @@ -212,6 +219,20 @@ public boolean setupSentryAppender(Level logLevel) { } SentryAppender appender = new SentryAppender(); SentryOptions opts = new SentryOptions(); + if (appenderConfig.isDebugEnabled()) { + opts.setDebug(true); + opts.setLogger(new SystemOutLogger()); + opts.setDiagnosticLevel(SentryLevel.ERROR); + } + if (logRoot == null) { + opts.setCacheDirPath("sentry"); + } else { + opts.setCacheDirPath(logRoot.resolve(".sentry").toAbsolutePath().toString()); + } + if (appenderConfig.getFlushTimeoutMs() != null) { + opts.setFlushTimeoutMillis(appenderConfig.getFlushTimeoutMs()); + } + appender.setMinimumEventLevel(ch.qos.logback.classic.Level.convertAnSLF4JLevel(logLevel)); opts.setDsn(appenderConfig.getDsn()); appender.setOptions(opts); diff --git a/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java index 54aef2fd1bdd..95248a9f0d8b 100644 --- a/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java +++ b/lib/scala/logging-service-logback/src/main/java/org/enso/logging/LoggingServer.java @@ -22,10 +22,11 @@ public LoggingServer(int port) { public URI start(Level level, Path path, String prefix, Appender appender) { var lc = new LoggerContext(); var setup = LogbackSetup.forContext(lc, appender); - setup.setup(level, path, prefix, setup.getConfig()); + logServer = new SimpleSocketServer(lc, port); logServer.start(); try { + setup.setup(level, path, prefix, setup.getConfig()); return new URI(null, null, "localhost", port, null, null, null); } catch (URISyntaxException e) { throw new RuntimeException(e); diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/native-image.properties b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/native-image.properties index 9848711dc334..f3643d3a6bb6 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/native-image.properties +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/native-image.properties @@ -1 +1 @@ -Args=--initialize-at-run-time=com.typesafe.config.impl.ConfigImpl$EnvVariablesHolder,com.typesafe.config.impl.ConfigImpl$SystemPropertiesHolder,org.enso.loggingservice.LoggingServiceManager$ +Args=--initialize-at-run-time=com.typesafe.config.impl.ConfigImpl$EnvVariablesHolder,com.typesafe.config.impl.ConfigImpl$SystemPropertiesHolder diff --git a/lib/scala/project-manager/src/main/resources/application.conf b/lib/scala/project-manager/src/main/resources/application.conf index ac71f1ea6633..f2b7302ed9d0 100644 --- a/lib/scala/project-manager/src/main/resources/application.conf +++ b/lib/scala/project-manager/src/main/resources/application.conf @@ -25,10 +25,10 @@ logging-service { appenders = [ { name = "socket" - hostname = ${?ENSO_LOGSERVER_HOSTNAME} hostname = "localhost" - port = ${?ENSO_LOGSERVER_PORT} + hostname = ${?ENSO_LOGSERVER_HOSTNAME} port = 6000 + port = ${?ENSO_LOGSERVER_PORT} }, { name = "file", @@ -62,8 +62,8 @@ logging-service { name = "console" } ] - default-appender = ${?ENSO_LOGSERVER_APPENDER} default-appender = file + default-appender = ${?ENSO_LOGSERVER_APPENDER} } } From ea4a68cba9e57cc5105a5069eb36c46abfe8bd3c Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Sun, 3 Sep 2023 22:48:02 +0200 Subject: [PATCH 30/31] Fix native image build --- build.sbt | 4 +-- .../org/enso/launcher/reflect-config.json | 4 +++ .../org/enso/launcher/resource-config.json | 5 +++- .../java/org/enso/logger/LoggerSetup.java | 25 +++++++++++------ .../java/org/enso/logger/LogbackSetup.java | 2 -- .../enso/logging/LoggingServiceFactory.java | 27 ++++++++++++------- .../enso/projectmanager/reflect-config.json | 4 +++ .../enso/projectmanager/resource-config.json | 2 ++ .../projectmanager/boot/ProjectManager.scala | 6 ++--- 9 files changed, 52 insertions(+), 27 deletions(-) diff --git a/build.sbt b/build.sbt index 8917d2a98687..74df5adb8c7a 100644 --- a/build.sbt +++ b/build.sbt @@ -726,6 +726,7 @@ lazy val `logging-service-logback` = project libraryDependencies ++= Seq( "org.slf4j" % "slf4j-api" % slf4jVersion, "io.sentry" % "sentry-logback" % "6.28.0", + "io.sentry" % "sentry" % "6.28.0", "org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided" ) ++ logbackPkg ) @@ -891,9 +892,6 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) "zio.Runtime$", "zio.FiberRef$" ), - additionalOptions = Seq( - "--trace-class-initialization=com.typesafe.config.impl.ConfigImpl$SystemPropertiesHolder,com.typesafe.config.impl.ConfigImpl$EnvVariablesHolder" - ) ) .dependsOn(VerifyReflectionSetup.run) .dependsOn(installNativeImage) diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json index 73ca20dcc9e3..d44c38e93aac 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/reflect-config.json @@ -723,6 +723,10 @@ } ] }, + { + "name":"org.enso.logger.LogbackSetup", + "methods":[{"name":"","parameterTypes":[] }] + }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json index 044086ab6f65..01c3f1881636 100644 --- a/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json +++ b/engine/launcher/src/main/resources/META-INF/native-image/org/enso/launcher/resource-config.json @@ -5,7 +5,10 @@ { "pattern": "\\Qapplication.conf\\E" }, { "pattern": "\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, { "pattern": "\\Qreference.conf\\E" }, - { "pattern": "\\Qversion.conf\\E" } + { "pattern": "\\Qversion.conf\\E" }, + { "pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, + { "pattern": "\\QMETA-INF/services/org.enso.logger.LoggerSetup\\E" }, + { "pattern": "\\QMETA-INF/services/org.enso.logging.LogbackLoggingServiceFactory\\E" } ], "bundles": [] } diff --git a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java index 64bb34ee0e58..07cd36488dfa 100644 --- a/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java +++ b/lib/scala/logging-config/src/main/java/org/enso/logger/LoggerSetup.java @@ -8,16 +8,25 @@ /** Base class to be implemented by the underlying logging implementation. */ public abstract class LoggerSetup { - private static LoggerSetup _instance; - - static { - ServiceLoader loader = - ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); - _instance = loader.findFirst().get(); - } + private static volatile LoggerSetup _instance; + private static Object _lock = new Object(); public static LoggerSetup get() { - return _instance; + LoggerSetup result = _instance; + if (result == null) { + synchronized (_lock) { + result = _instance; + if (result == null) { + // Can't initialize in static initializer because Config has to be able to read runtime + // env vars + ServiceLoader loader = + ServiceLoader.load(LoggerSetup.class, LoggerSetup.class.getClassLoader()); + result = loader.findFirst().get(); + _instance = result; + } + } + } + return result; } /** Returns parsed application config used to create this instance * */ diff --git a/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java index 858249b7db2a..4a0343f8dc10 100644 --- a/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java +++ b/lib/scala/logging-service-logback/src/main/java/org/enso/logger/LogbackSetup.java @@ -12,8 +12,6 @@ import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.util.Duration; import ch.qos.logback.core.util.FileSize; import io.sentry.SentryLevel; diff --git a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java index 50ff3efa08fc..97513a8fa80c 100644 --- a/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java +++ b/lib/scala/logging-service/src/main/java/org/enso/logging/LoggingServiceFactory.java @@ -5,19 +5,28 @@ public abstract class LoggingServiceFactory { - private static LoggingServiceFactory _loggingServiceFactory; - - static { - ServiceLoader loader = - ServiceLoader.load( - LoggingServiceFactory.class, LoggingServiceFactory.class.getClassLoader()); - _loggingServiceFactory = loader.findFirst().get(); - } + private static volatile LoggingServiceFactory _loggingServiceFactory; + private static Object _lock = new Object(); public abstract LoggingService localServerFor(int port); @SuppressWarnings("unchecked") public static LoggingServiceFactory get() { - return _loggingServiceFactory; + LoggingServiceFactory result = _loggingServiceFactory; + if (result == null) { + synchronized (_lock) { + result = _loggingServiceFactory; + if (result == null) { + // Can't initialize in static initializer because Config has to be able to read runtime + // env vars + ServiceLoader loader = + ServiceLoader.load( + LoggingServiceFactory.class, LoggingServiceFactory.class.getClassLoader()); + result = loader.findFirst().get(); + _loggingServiceFactory = result; + } + } + } + return result; } } diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json index bb81ecc57744..16f472b6eacc 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/reflect-config.json @@ -566,6 +566,10 @@ } ] }, + { + "name":"org.enso.logger.LogbackSetup", + "methods":[{"name":"","parameterTypes":[] }] + }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] diff --git a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json index c25e59bb8678..cdda671e57d4 100644 --- a/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json +++ b/lib/scala/project-manager/src/main/resources/META-INF/native-image/org/enso/projectmanager/resource-config.json @@ -51,6 +51,8 @@ "pattern": "\\Qorg/enso/projectmanager/service/versionmanagement/RuntimeVersionManagerMixin$ErrorRecovery.class\\E" }, { "pattern": "\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" }, + { "pattern": "\\QMETA-INF/services/org.enso.logger.LoggerSetup\\E" }, + { "pattern": "\\QMETA-INF/services/org.enso.logging.LogbackLoggingServiceFactory\\E" }, { "pattern": "\\Qch/qos/logback/classic/spi/Configurator.class\\E" }, { "pattern": "\\Qreference.conf\\E" }, { "pattern": "\\Qversion.conf\\E" }, 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 9e8fa34416af..ef371d8bc03d 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 @@ -234,11 +234,8 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { } else { val verbosity = options.getOptions.count(_ == Cli.option.verbose) val logMasking = !options.hasOption(Cli.NO_LOG_MASKING) - logger.info( - "Starting {}", - makeVersionDescription.asString(useJson = false) - ) for { + _ <- displayVersion(false) opts <- parseOpts(options) logLevel <- setupLogging(verbosity, logMasking) procConf = MainProcessConfig( @@ -270,6 +267,7 @@ object ProjectManager extends ZIOAppDefault with LazyLogging { ZIO .attempt { Logging.setup(level, logMasking) + Logging.waitForSetup() () } .catchAll { exception => From ffb40f27a58907d4b4048eeba14ead3e65cc9663 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 4 Sep 2023 10:17:03 +0200 Subject: [PATCH 31/31] formatting --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 74df5adb8c7a..96c6a256a5a4 100644 --- a/build.sbt +++ b/build.sbt @@ -891,7 +891,7 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager")) "zio.internal.ZScheduler$$anon$4", "zio.Runtime$", "zio.FiberRef$" - ), + ) ) .dependsOn(VerifyReflectionSetup.run) .dependsOn(installNativeImage)