Implement masking of sensitive data in logs (#1732)

This commit is contained in:
Dmitry Bushev 2021-05-12 18:31:53 +03:00 committed by GitHub
parent c4c483683e
commit 1b6388702f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 1080 additions and 509 deletions

View File

@ -7,6 +7,9 @@
searcher to make more accurate suggestions when working with collection types.
- Fixed an issue where symlinks were not extracted properly when installing a
runtime for Enso ([#1718](https://github.com/enso-org/enso/pull/1718)).
- Implemented log masking ([#1732](https://github.com/enso-org/enso/pull/1732)).
This feature masks personally identifiable information in the logs, such as
code literals, computed values, and user environment variables.
## Libraries

View File

@ -190,6 +190,7 @@ lazy val enso = (project in file("."))
logger.jvm,
pkg,
cli,
`logging-utils`,
`logging-service`,
`akka-native`,
`version-output`,
@ -602,6 +603,16 @@ lazy val `akka-native` = project
libraryDependencies += "org.graalvm.nativeimage" % "svm" % graalVersion % "provided"
)
lazy val `logging-utils` = project
.in(file("lib/scala/logging-utils"))
.configs(Test)
.settings(
version := "0.1",
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
)
)
lazy val `logging-service` = project
.in(file("lib/scala/logging-service"))
.configs(Test)
@ -625,6 +636,7 @@ lazy val `logging-service` = project
(Compile / unmanagedSourceDirectories) += (Compile / sourceDirectory).value / "java-unix"
)
.dependsOn(`akka-native`)
.dependsOn(`logging-utils`)
lazy val cli = project
.in(file("lib/scala/cli"))
@ -1063,6 +1075,7 @@ lazy val runtime = (project in file("engine/runtime"))
.dependsOn(`text-buffer`)
.dependsOn(searcher)
.dependsOn(testkit % Test)
.dependsOn(`logging-utils`)
/* Note [Unmanaged Classpath]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -23,6 +23,8 @@ components.
- [JVM Architecture](#jvm-architecture)
- [SLF4J Interface](#slf4j-interface)
- [Setting Up Logging](#setting-up-logging)
- [Log Masking](#log-masking)
- [Logging in Tests](#logging-in-tests)
<!-- /MarkdownTOC -->
@ -252,6 +254,32 @@ 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.
### Log Masking
Logs should not contain personally identifiable information (PII). The following
is considered PII:
- User code
- Values of executed expressions
- Values of user environment variables. Although variable names are not
considered PII and can be logged.
- File paths inside the user project directory. System and distribution paths
and a path to the user project can be logged.
Project logging library implements masking of PII. To utilize it
1. Logged object should implement an interface that defines custom log-string
representation of this object
2. The logging should be performed by supplying a template string with `{}`
placeholders, and the arguments
```scala
log.debug("Created {} at [{}].", obj, path)
```
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.
### Logging in Tests
The Logging Service provides several utilities for managing logs inside of

View File

@ -56,8 +56,8 @@ independent Scalafmt plugin.
## Naming
Enso has some fairly simple general naming conventions, though the sections
below may provide more rules for use in specific cases.
Enso has some fairly simple general naming conventions, though the sections w
may provide more rules for use in specific cases.
- Types are written using `UpperCamelCase`.
- Variables and function names are written using `camelCase`.

View File

@ -1,17 +1,14 @@
package org.enso.languageserver.boot
/** Signal where the lang. server is deployed.
*/
/** Signal where the lang. server is deployed. */
sealed trait DeploymentType
object DeploymentType {
/** Desktop deployment.
*/
/** Desktop deployment. */
case object Desktop extends DeploymentType
/** Azure deployment.
*/
/** Azure deployment. */
case object Azure extends DeploymentType
/** Determines the current deployment type from environment variables.

View File

@ -1,7 +1,11 @@
package org.enso.languageserver.boot
import java.nio.file.Path
import java.util.UUID
import org.enso.logger.masking.MaskingUtils.toMaskedPath
import org.enso.logger.masking.ToLogString
import scala.concurrent.ExecutionContext
/** The config of the running Language Server instance.
@ -20,4 +24,24 @@ case class LanguageServerConfig(
contentRootPath: String,
name: String = "language-server",
computeExecutionContext: ExecutionContext = ExecutionContext.global
)
) extends ToLogString {
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String = {
val contentRootString =
if (shouldMask) {
toMaskedPath(Path.of(contentRootPath))
} else {
contentRootPath
}
s"LanguageServerConfig(" +
s"interface=$interface, " +
s"rpcPort=$rpcPort, " +
s"dataPort=$dataPort, " +
s"contentRootUuid=$contentRootUuid, " +
s"contentRootPath=$contentRootString, " +
s"name=$name, " +
s"computeExecutionContext=$computeExecutionContext" +
s")"
}
}

View File

@ -49,10 +49,11 @@ import scala.concurrent.duration._
*/
class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
val log = LoggerFactory.getLogger(this.getClass)
private val log = LoggerFactory.getLogger(this.getClass)
log.info(
s"Initializing main module of the Language Server from " +
s"$serverConfig $logLevel."
"Initializing main module of the Language Server from [{}, {}]",
serverConfig,
logLevel
)
val directoriesConfig = DirectoriesConfig(serverConfig.contentRootPath)
@ -63,17 +64,17 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
ExecutionContextConfig(),
directoriesConfig
)
log.trace(s"Created Language Server config $languageServerConfig.")
log.trace("Created Language Server config [{}].", languageServerConfig)
val zioExec = ZioExec(zio.Runtime.default)
log.trace(s"Created ZIO executor $zioExec.")
log.trace("Created ZIO executor [{}].", zioExec)
val fileSystem: FileSystem = new FileSystem
log.trace(s"Created file system $fileSystem.")
log.trace("Created file system [{}].", fileSystem)
implicit val versionCalculator: ContentBasedVersioning =
Sha3_224VersionCalculator
log.trace(s"Created Version Calculator $versionCalculator.")
log.trace("Created Version Calculator [{}].", versionCalculator)
implicit val system =
ActorSystem(
@ -100,7 +101,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
val versionsRepo = new SqlVersionsRepo(sqlDatabase)(system.dispatcher)
log.trace(s"Created SQL repos: $suggestionsRepo, $versionsRepo.")
log.trace("Created SQL repos: [{}. {}].", suggestionsRepo, versionsRepo)
lazy val sessionRouter =
system.actorOf(SessionRouter.props(), "session-router")
@ -197,10 +198,10 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
} else null
})
.build()
log.trace(s"Created Runtime context $context.")
log.trace("Created Runtime context [{}].", context)
system.eventStream.setLogLevel(LogLevel.toAkka(logLevel))
log.trace(s"Set akka log level to $logLevel.")
log.trace("Set akka log level to [{}].", logLevel)
val runtimeKiller =
system.actorOf(
@ -249,7 +250,8 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
runtimeConnector
)
log.trace(
s"Created JSON connection controller factory $jsonRpcControllerFactory."
"Created JSON connection controller factory [{}].",
jsonRpcControllerFactory
)
val pingHandlerProps =
@ -277,7 +279,7 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
.Config(outgoingBufferSize = 10000, lazyMessageTimeout = 10.seconds),
List(healthCheckEndpoint)
)
log.trace(s"Created JSON RPC Server $jsonRpcServer.")
log.trace("Created JSON RPC Server [{}].", jsonRpcServer)
val binaryServer =
new BinaryWebSocketServer(
@ -285,11 +287,11 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
BinaryEncoder.empty,
new BinaryConnectionControllerFactory(fileManager)
)
log.trace(s"Created Binary WebSocket Server $binaryServer.")
log.trace("Created Binary WebSocket Server [{}].", binaryServer)
log.info(
s"Main module of the Language Server initialized " +
s"with config $languageServerConfig."
"Main module of the Language Server initialized with config [{}].",
languageServerConfig
)
/** Close the main module releasing all resources. */

View File

@ -7,6 +7,7 @@ import akka.event.EventStream
import org.apache.commons.io.FileUtils
import org.enso.languageserver.data.DirectoriesConfig
import org.enso.languageserver.event.InitializedEvent
import org.enso.logger.masking.MaskedPath
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
import org.slf4j.LoggerFactory
@ -43,8 +44,8 @@ class RepoInitialization(
for {
_ <- Future {
log.info(
s"Initializing suggestions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}."
"Initializing suggestions repo [{}].",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath)
)
}
_ <- suggestionsRepo.init.recoverWith { case NonFatal(error) =>
@ -52,8 +53,8 @@ class RepoInitialization(
}
_ <- Future {
log.info(
s"Initialized Suggestions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}."
"Initialized Suggestions repo [{}].",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath)
)
}
} yield ()
@ -62,9 +63,9 @@ class RepoInitialization(
eventStream.publish(InitializedEvent.SuggestionsRepoInitialized)
case Failure(ex) =>
log.error(
s"Failed to initialize SQL suggestions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}. " +
s"${ex.getMessage}"
"Failed to initialize SQL suggestions repo [{}]. {}",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath),
ex.getMessage
)
}
initAction
@ -75,15 +76,15 @@ class RepoInitialization(
for {
_ <- Future {
log.info(
s"Initializing versions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}."
"Initializing versions repo [{}].",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath)
)
}
_ <- versionsRepo.init
_ <- Future {
log.info(
s"Initialized Versions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}."
"Initialized Versions repo [{}].",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath)
)
}
} yield ()
@ -92,9 +93,9 @@ class RepoInitialization(
eventStream.publish(InitializedEvent.FileVersionsRepoInitialized)
case Failure(ex) =>
log.error(
s"Failed to initialize SQL versions repo " +
s"${directoriesConfig.suggestionsDatabaseFile}. " +
s"${ex.getMessage}"
"Failed to initialize SQL versions repo [{}]. {}",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath),
ex.getMessage
)
}
initAction
@ -107,9 +108,9 @@ class RepoInitialization(
for {
_ <- Future {
log.warn(
s"Failed to initialize the suggestions database " +
s"${directoriesConfig.suggestionsDatabaseFile}. " +
s"${error.getMessage}"
"Failed to initialize the suggestions database [{}]. {}",
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath),
error.getMessage
)
}
_ <- Future(db.close())
@ -123,14 +124,15 @@ class RepoInitialization(
private def clearDatabaseFile(retries: Int = 0): Future[Unit] = {
Future {
log.info(s"Clear database file. Attempt #${retries + 1}.")
log.info("Clear database file. Attempt #{}.", retries + 1)
Files.delete(directoriesConfig.suggestionsDatabaseFile.toPath)
}.recoverWith {
case _: NoSuchFileException =>
log.warn(
s"Failed to delete the database file. Attempt #${retries + 1}. " +
s"File does not exist " +
s"${directoriesConfig.suggestionsDatabaseFile}"
"Failed to delete the database file. Attempt #{}. " +
"File does not exist [{}].",
retries + 1,
MaskedPath(directoriesConfig.suggestionsDatabaseFile.toPath)
)
Future.successful(())
case error: FileSystemException =>
@ -144,8 +146,9 @@ class RepoInitialization(
Future.failed(error)
case error: IOException =>
log.error(
s"Failed to delete the database file. Attempt #${retries + 1}. " +
s"${error.getMessage}"
"Failed to delete the database file. Attempt #{}. {}",
retries + 1,
error.getMessage
)
if (retries < RepoInitialization.MaxRetries) {
Thread.sleep(1000)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
FileSystemFailure,
Path
}
import org.enso.logger.masking.{MaskingUtils, ToLogString}
import scala.concurrent.duration._
@ -77,7 +78,7 @@ object ExecutionContextConfig {
*
* @param root the root directory path
*/
case class DirectoriesConfig(root: File) {
case class DirectoriesConfig(root: File) extends ToLogString {
/** The data directory path. */
val dataDirectory: File =
@ -87,6 +88,14 @@ case class DirectoriesConfig(root: File) {
val suggestionsDatabaseFile: File =
new File(dataDirectory, DirectoriesConfig.SuggestionsDatabaseFile)
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String = {
val rootString =
if (shouldMask) MaskingUtils.toMaskedPath(root.toPath)
else root.toString
s"DirectoriesConfig($rootString)"
}
/** Create data directories if not exist. */
def createDirectories(): Unit =
Files.createDirectories(dataDirectory.toPath)
@ -127,7 +136,27 @@ case class Config(
pathWatcher: PathWatcherConfig,
executionContext: ExecutionContextConfig,
directories: DirectoriesConfig
) {
) extends ToLogString {
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String = {
val maskedRoots =
if (shouldMask) {
contentRoots
.map { case (k, v) =>
k -> MaskingUtils.toMaskedPath(v.toPath)
}
} else {
contentRoots
}
s"Config(" +
s"contentRoots=$maskedRoots, " +
s"fileManager=$fileManager, " +
s"pathWatcher=$pathWatcher, " +
s"executionContext=$executionContext, " +
s"directories=${directories.toLogString(shouldMask)}" +
s")"
}
def findContentRoot(rootId: UUID): Either[FileSystemFailure, File] =
contentRoots

View File

@ -9,7 +9,7 @@ import java.time.OffsetDateTime
* @param lastAccessTime last access time
* @param lastModifiedTime last modified time
* @param kind type of [[FileSystemObject]], can be
* [[FileSystemObject.DirectoryTruncated]]
* [[FileSystemObject.SymlinkLoop]]
* | [[FileSystemObject.File]]
* | [[FileSystemObject.Other]]
* @param byteSize size in bytes
@ -28,7 +28,7 @@ object FileAttributes {
*
* @param root a root path
* @param path a path to the file system object
* @param attra file system attributes
* @param attrs file system attributes
* @return file attributes
*/
def fromFileSystemAttributes(

View File

@ -25,18 +25,15 @@ final class WatcherAdapter(
.listener(this)
.build()
/** Start watcher.
*/
/** Start watcher. */
def start(): IO[Throwable, Unit] =
IO(watcher.watch())
/** Stop watcher.
*/
/** Stop watcher. */
def stop(): IO[Throwable, Unit] =
IO(watcher.close())
/** A callback executed by `DirectoryWatcher` on file system event.
*/
/** A callback executed by `DirectoryWatcher` on file system event. */
override def onEvent(event: DirectoryChangeEvent): Unit = {
WatcherEvent
.from(event)
@ -50,8 +47,7 @@ final class WatcherAdapter(
object WatcherAdapter {
/** Type of a file event.
*/
/** Type of a file event. */
sealed trait EventType
private object EventType {
@ -70,16 +66,13 @@ object WatcherAdapter {
}
}
/** Event type indicating file creation.
*/
/** Event type indicating file creation. */
case object EventTypeCreate extends EventType
/** Event type indicating file modification.
*/
/** Event type indicating file modification. */
case object EventTypeModify extends EventType
/** Event type indicating file deletion.
*/
/** Event type indicating file deletion. */
case object EventTypeDelete extends EventType
/** Object representing file system event.

View File

@ -121,7 +121,9 @@ class BinaryWebSocketServer[A, B](
.mapConcat[BinaryMessage] {
case msg: TextMessage =>
logger.warn(
s"Received text message $msg over the data connection [$ip]"
"Received text message [{}] over the data connection [{}].",
msg,
ip
)
Nil

View File

@ -48,7 +48,7 @@ class InputRedirectionController(
private def running(liveContexts: Set[ContextData] = Set.empty): Receive = {
case FeedStandardInput(input, isLineTerminated) =>
log.debug(s"Feeding stdin [${input.length} bytes]")
log.debug("Feeding stdin [{} bytes]", input.length)
if (isLineTerminated) {
val bytes =
ByteString.createBuilder
@ -68,7 +68,7 @@ class InputRedirectionController(
context.become(running(liveContexts - ContextData(contextId, owner)))
case ReadBlocked =>
log.debug("Blocked read detected")
log.debug("Blocked read detected.")
liveContexts foreach { case ContextData(_, owner) =>
sessionRouter ! DeliverToJsonController(
owner,

View File

@ -87,7 +87,11 @@ class BinaryConnectionController(
outboundChannel ! responsePacket
val session = BinarySession(clientId, self)
context.system.eventStream.publish(BinarySessionInitialized(session))
log.info(s"Data session initialized for client: $clientId [$clientIp]")
log.info(
"Data session initialized for client: {} [{}].",
clientId,
clientIp
)
context.become(
connectionEndHandler(Some(session))
orElse initialized(
@ -111,7 +115,8 @@ class BinaryConnectionController(
handler.forward(msg)
} else {
log.error(
s"Received InboundMessage with unknown payload type: ${msg.payloadType()}"
"Received InboundMessage with unknown payload type [{}].",
msg.payloadType()
)
}
@ -124,7 +129,7 @@ class BinaryConnectionController(
maybeDataSession: Option[BinarySession] = None
): Receive = {
case ConnectionClosed =>
log.info(s"Connection closed [$clientIp]")
log.info("Connection closed [{}].", clientIp)
maybeDataSession.foreach(session =>
context.system.eventStream.publish(BinarySessionTerminated(session))
)
@ -132,8 +137,9 @@ class BinaryConnectionController(
case ConnectionFailed(th) =>
log.error(
s"An error occurred during processing web socket connection [$clientIp]",
th
th,
"An error occurred during processing web socket connection [{}].",
clientIp
)
maybeDataSession.foreach(session =>
context.system.eventStream.publish(BinarySessionTerminated(session))
@ -154,7 +160,7 @@ class BinaryConnectionController(
case EmptyPayload => ErrorFactory.createReceivedEmptyPayloadError()
case DataCorrupted => ErrorFactory.createReceivedCorruptedDataError()
case GenericDecodingFailure(th) =>
log.error("Unrecognized error occurred in binary protocol", th)
log.error(th, "Unrecognized error occurred in binary protocol.")
ErrorFactory.createServiceError()
}

View File

@ -129,7 +129,7 @@ class JsonConnectionController(
_,
InitProtocolConnection.Params(clientId)
) =>
log.info("Initializing resources")
log.info("Initializing resources.")
mainComponent.init().pipeTo(self)
context.become(initializing(webActor, clientId, req, sender()))
@ -147,7 +147,7 @@ class JsonConnectionController(
receiver: ActorRef
): Receive = {
case InitializationComponent.Initialized =>
log.info(s"RPC session initialized for client: $clientId")
log.info("RPC session initialized for client [{}].", clientId)
val session = JsonSession(clientId, self)
context.system.eventStream.publish(JsonSessionInitialized(session))
val requestHandlers = createRequestHandlers(session)
@ -159,7 +159,7 @@ class JsonConnectionController(
context.become(initialised(webActor, session, requestHandlers))
case Status.Failure(ex) =>
log.error(ex, "Failed to initialize the resources")
log.error(ex, "Failed to initialize the resources. {}", ex.getMessage)
receiver ! ResponseError(Some(request.id), ResourcesInitializationError)
context.become(connected(webActor))

View File

@ -52,7 +52,10 @@ class AcquireCapabilityHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Acquiring capability for ${session.clientId} timed out")
log.error(
"Acquiring capability for client [{}] timed out.",
session.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -50,7 +50,10 @@ class ReleaseCapabilityHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Releasing capability for ${session.clientId} timed out")
log.error(
"Releasing capability for client [{}] timed out.",
session.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -51,7 +51,7 @@ class CreateHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -50,7 +50,7 @@ class DestroyHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -46,7 +46,7 @@ class PopHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -54,7 +54,7 @@ class PushHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -54,7 +54,7 @@ class RecomputeHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.CopyFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class CopyFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $CopyFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
CopyFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.CreateFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class CreateFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $CreateFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
CreateFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -11,6 +11,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.DeleteFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -38,13 +39,17 @@ class DeleteFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
path: Path
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $DeleteFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
DeleteFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.ExistsFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class ExistsFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $ExistsFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
ExistsFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.InfoFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -40,13 +41,17 @@ class InfoFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $InfoFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
InfoFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.ListFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class ListFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $ListFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
ListFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.MoveFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class MoveFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $MoveFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
MoveFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -17,6 +17,7 @@ import org.enso.languageserver.protocol.binary.factory.{
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.languageserver.util.file.PathUtils
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -53,14 +54,17 @@ class ReadBinaryFileHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during ReadBinaryFile operation:", ex)
log.error(
s"Failure during ReadBinaryFile operation: {}",
MaskedString(ex.getMessage)
)
val packet = ErrorFactory.createServiceError(Some(requestId))
replyTo ! packet
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request ReadBinaryFile timed out")
log.error("Request ReadBinaryFile [{}] timed out.", requestId)
val packet = ErrorFactory.createServiceError(Some(requestId))
replyTo ! packet
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.ReadFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -37,13 +38,17 @@ class ReadTextualFileHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $ReadFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
ReadFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.TreeFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -35,13 +36,17 @@ class TreeFileHandler(requestTimeout: FiniteDuration, fileManager: ActorRef)
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $TreeFile operation:", ex)
log.error(
"Failure during [{}] operation: {}",
TreeFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -17,6 +17,7 @@ import org.enso.languageserver.protocol.binary.factory.{
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.languageserver.util.file.PathUtils
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -56,14 +57,17 @@ class WriteBinaryFileHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during WriteBinaryFile operation:", ex)
log.error(
"Failure during WriteBinaryFile operation: {}",
MaskedString(ex.getMessage)
)
val packet = ErrorFactory.createServiceError(Some(requestId))
replyTo ! packet
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request WriteBinaryFile timed out")
log.error("Request WriteBinaryFile [{}] timed out.", requestId)
val packet = ErrorFactory.createServiceError(Some(requestId))
replyTo ! packet
context.stop(self)

View File

@ -9,6 +9,7 @@ import org.enso.languageserver.filemanager.{
import org.enso.languageserver.filemanager.FileManagerApi.WriteFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString
import scala.concurrent.duration.FiniteDuration
@ -37,13 +38,17 @@ class WriteTextualFileHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(s"Failure during $WriteFile operation:", ex)
log.error(
s"Failure during [{}] operation: {}",
WriteFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
context.stop(self)

View File

@ -49,7 +49,9 @@ class PingHandler(
): Receive = {
case RequestTimeout =>
log.error(
s"Health check timed out. Only $count/${subsystems.size} subsystems replied on time."
"Health check timed out. Only {}/{} subsystems replied on time.",
count,
subsystems.size
)
if (shouldReplyWhenTimedOut) {
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)

View File

@ -49,7 +49,7 @@ class RenameProjectHandler(timeout: FiniteDuration, runtimeConnector: ActorRef)
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -52,13 +52,13 @@ class CompletionHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, "Search completion error")
log.error(ex, "Search completion error: {}", ex.getMessage)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -42,13 +42,13 @@ class GetSuggestionsDatabaseHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, "GetSuggestionsDatabase error")
log.error(ex, "GetSuggestionsDatabase error: {}", ex.getMessage)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -42,13 +42,13 @@ class GetSuggestionsDatabaseVersionHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, "GetSuggestionsDatabaseVersion error")
log.error(ex, "GetSuggestionsDatabaseVersion error: {}", ex.getMessage)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -42,13 +42,13 @@ class ImportHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, "Search import error")
log.error(ex, "Search import error: {}", ex.getMessage)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -46,13 +46,13 @@ class InvalidateSuggestionsDatabaseHandler(
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, "InvalidateSuggestionsDatabase error")
log.error(ex, "InvalidateSuggestionsDatabase error: {}", ex.getMessage)
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -40,7 +40,7 @@ class InitProtocolConnectionHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Getting content roots timed out")
log.error("Getting content roots request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -43,7 +43,11 @@ class ApplyEditHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Applying edit for ${rpcSession.clientId} timed out")
log.error(
"Applying edit request [{}] for [{}] timed out.",
id,
rpcSession.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -43,7 +43,11 @@ class CloseFileHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Closing file for ${rpcSession.clientId} timed out")
log.error(
"Closing file request [{}] for [{}] timed out.",
id,
rpcSession.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -47,7 +47,11 @@ class OpenFileHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Opening file for ${rpcSession.clientId} timed out")
log.error(
"Opening file request [{}] for [{}] timed out.",
id,
rpcSession.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -53,7 +53,11 @@ class SaveFileHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Saving file for ${rpcSession.clientId} timed out")
log.error(
"Saving file request [{}] for [{}] timed out.",
id,
rpcSession.clientId
)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -50,7 +50,7 @@ class AttachVisualisationHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -50,7 +50,7 @@ class DetachVisualisationHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -49,7 +49,7 @@ class ModifyVisualisationHandler(
cancellable: Cancellable
): Receive = {
case RequestTimeout =>
log.error(s"Request $id timed out")
log.error("Request [{}] timed out.", id)
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
context.stop(self)

View File

@ -157,7 +157,7 @@ final class ContextEventsListener(
methodPointerToSuggestion.get(pointer) match {
case suggestionId @ Some(_) => suggestionId
case None =>
log.error(s"Unable to find suggestion for $pointer")
log.error("Unable to find suggestion for [{}].", pointer)
None
}
},

View File

@ -23,8 +23,8 @@ class RuntimeConnector
override def receive: Receive = {
case RuntimeConnector.Initialize(engine) =>
log.info(
s"Runtime connector established connection with the message endpoint " +
s"$engine."
s"Runtime connector established connection with the message endpoint [{}].",
engine
)
unstashAll()
context.become(initialized(engine, Map()))

View File

@ -62,8 +62,10 @@ class RuntimeKiller(runtimeConnector: ActorRef, truffleContext: Context)
private def shutDownTruffle(replyTo: ActorRef, retryCount: Int = 0): Unit = {
try {
log.info(
s"Shutting down the Truffle context $truffleContext. " +
s"Attempt #${retryCount + 1}."
"Shutting down the Truffle context [{}]. " +
"Attempt #{}.",
truffleContext,
retryCount + 1
)
truffleContext.close()
replyTo ! RuntimeGracefullyStopped
@ -72,8 +74,9 @@ class RuntimeKiller(runtimeConnector: ActorRef, truffleContext: Context)
case NonFatal(ex) =>
log.error(
ex,
s"An error occurred during stopping Truffle context " +
s"$truffleContext. ${ex.getMessage}"
s"An error occurred during stopping Truffle context [{}]. {}",
truffleContext,
ex.getMessage
)
if (retryCount < MaxRetries) {
context.system.scheduler

View File

@ -26,6 +26,7 @@ import org.enso.languageserver.search.handler.{
}
import org.enso.languageserver.session.SessionRouter.DeliverToJsonController
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedPath
import org.enso.pkg.PackageManager
import org.enso.polyglot.Suggestion
import org.enso.polyglot.data.TypeGraph
@ -93,9 +94,10 @@ final class SuggestionsHandler(
override def preStart(): Unit = {
log.info(
s"Starting suggestions handler from $config " +
s"$suggestionsRepo $fileVersionsRepo " +
s"$sessionRouter $runtimeConnector"
"Starting suggestions handler from [{}, {}, {}].",
config,
suggestionsRepo,
fileVersionsRepo
)
context.system.eventStream
.subscribe(self, classOf[Api.ExpressionUpdates])
@ -120,19 +122,23 @@ final class SuggestionsHandler(
def initializing(init: SuggestionsHandler.Initialization): Receive = {
case ProjectNameChangedEvent(oldName, newName) =>
log.info(s"Initializing: project name changed from $oldName to $newName.")
log.info(
"Initializing: project name changed from [{}] to [{}].",
oldName,
newName
)
suggestionsRepo
.renameProject(oldName, newName)
.map(_ => ProjectNameUpdated(newName))
.pipeTo(self)
case ProjectNameUpdated(name, updates) =>
log.info(s"Initializing: project name is updated to $name.")
log.info("Initializing: project name is updated to [{}].", name)
updates.foreach(sessionRouter ! _)
tryInitialize(init.copy(project = Some(name)))
case InitializedEvent.SuggestionsRepoInitialized =>
log.info(s"Initializing: suggestions repo initialized.")
log.info("Initializing: suggestions repo initialized.")
tryInitialize(
init.copy(suggestions =
Some(InitializedEvent.SuggestionsRepoInitialized)
@ -140,14 +146,14 @@ final class SuggestionsHandler(
)
case InitializedEvent.TruffleContextInitialized =>
log.info(s"Initializing: Truffle context initialized.")
log.info("Initializing: Truffle context initialized.")
val requestId = UUID.randomUUID()
runtimeConnector
.ask(Api.Request(requestId, Api.GetTypeGraphRequest()))(timeout, self)
.pipeTo(self)
case Api.Response(_, Api.GetTypeGraphResponse(g)) =>
log.info(s"Initializing: got type graph response.")
log.info("Initializing: got type graph response.")
tryInitialize(init.copy(typeGraph = Some(g)))
case _ => stash()
@ -158,14 +164,14 @@ final class SuggestionsHandler(
graph: TypeGraph
): Receive = {
case Api.Response(_, Api.VerifyModulesIndexResponse(toRemove)) =>
log.info(s"Verifying: got verification response.")
log.info("Verifying: got verification response.")
suggestionsRepo
.removeModules(toRemove)
.map(_ => SuggestionsHandler.Verified)
.pipeTo(self)
case SuggestionsHandler.Verified =>
log.info(s"Verified.")
log.info("Verified.")
context.become(initialized(projectName, graph, Set()))
unstashAll()
@ -213,15 +219,18 @@ final class SuggestionsHandler(
case Success(None) =>
case Failure(ex) =>
log.error(
s"Error applying suggestion database updates" +
s" ${msg.file} ${msg.version}. ${ex.getMessage}"
ex,
"Error applying suggestion database updates [{}, {}]. {}",
MaskedPath(msg.file.toPath),
msg.version,
ex.getMessage
)
}
case Api.ExpressionUpdates(_, updates) =>
log.debug(
s"Received expression updates: " +
s"${updates.map(u => (u.expressionId, u.expressionType))}"
"Received expression updates [{}].",
updates.map(u => (u.expressionId, u.expressionType))
)
val types = updates.toSeq
.flatMap(update => update.expressionType.map(update.expressionId -> _))
@ -246,8 +255,10 @@ final class SuggestionsHandler(
}
case Failure(ex) =>
log.error(
s"Error applying changes from computed values: $updates. " +
s"${ex.getMessage}"
ex,
"Error applying changes from computed values [{}]. {}",
updates.map(_.expressionId),
ex.getMessage
)
}
@ -320,13 +331,14 @@ final class SuggestionsHandler(
}
case Success(Left(err)) =>
log.error(
s"Error cleaning the index after file delete event. " +
s"$err"
s"Error cleaning the index after file delete event [{}].",
err
)
case Failure(ex) =>
log.error(
s"Error cleaning the index after file delete event. " +
s"${ex.getMessage}"
ex,
"Error cleaning the index after file delete event. {}",
ex.getMessage
)
}
@ -403,7 +415,7 @@ final class SuggestionsHandler(
private def tryInitialize(state: SuggestionsHandler.Initialization): Unit = {
state.initialized.fold(context.become(initializing(state))) {
case (name, graph) =>
log.debug(s"Initialized with state $state.")
log.debug("Initialized with state [{}].", state)
val requestId = UUID.randomUUID()
suggestionsRepo.getAllModules
.flatMap { modules =>
@ -442,10 +454,12 @@ final class SuggestionsHandler(
val verb = action.getClass.getSimpleName
action match {
case Api.SuggestionAction.Add() =>
if (ids.isEmpty) log.error(s"Failed to $verb: $suggestion")
if (ids.isEmpty)
log.error("Failed to {} [{}].", verb, suggestion)
ids.map(id => SuggestionsDatabaseUpdate.Add(id, suggestion))
case Api.SuggestionAction.Remove() =>
if (ids.isEmpty) log.error(s"Failed to $verb: $suggestion")
if (ids.isEmpty)
log.error(s"Failed to {} [{}].", verb, suggestion)
ids.map(id => SuggestionsDatabaseUpdate.Remove(id))
case m: Api.SuggestionAction.Modify =>
ids.map { id =>

View File

@ -67,7 +67,7 @@ class CollaborativeBuffer(
private def uninitialized: Receive = { case OpenFile(client, path) =>
context.system.eventStream.publish(BufferOpened(path))
log.info(s"Buffer opened for $path [client:${client.clientId}]")
log.info("Buffer opened for [path:{}, client:{}].", path, client.clientId)
readFile(client, path)
}

View File

@ -35,6 +35,7 @@ import org.enso.version.{VersionDescription, VersionDescriptionParameter}
* @param cliOptions the global CLI options to use for the commands
*/
case class Launcher(cliOptions: GlobalCLIOptions) {
private val logger = Logger[Launcher]
private lazy val componentsManager =
@ -89,9 +90,6 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
.get,
JVMSettings(useSystemJVM, jvmOpts)
) { command =>
logger.trace(
s"Executing a command that creates a new project: $command"
)
command.run().get
}
@ -209,11 +207,16 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
val exitCode = runner
.withCommand(
runner
.repl(projectPath, versionOverride, logLevel, additionalArguments)
.repl(
projectPath,
versionOverride,
logLevel,
cliOptions.internalOptions.logMasking,
additionalArguments
)
.get,
JVMSettings(useSystemJVM, jvmOpts)
) { command =>
logger.trace(s"Executing a command that starts the REPL: $command")
command.run().get
}
exitCode
@ -247,12 +250,17 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
): Int = {
val exitCode = runner
.withCommand(
runner.run(path, versionOverride, logLevel, additionalArguments).get,
runner
.run(
path,
versionOverride,
logLevel,
cliOptions.internalOptions.logMasking,
additionalArguments
)
.get,
JVMSettings(useSystemJVM, jvmOpts)
) { command =>
logger.trace(
s"Executing a command that runs the Enso program: $command"
)
command.run().get
}
exitCode
@ -295,9 +303,6 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
.get,
JVMSettings(useSystemJVM, jvmOpts)
) { command =>
logger.trace(
s"Executing a command that starts the Language Server: $command"
)
command.run().get
}
exitCode
@ -441,10 +446,6 @@ case class Launcher(cliOptions: GlobalCLIOptions) {
runtimeVersionRunSettings,
JVMSettings(useSystemJVM = false, jvmOptions = Seq.empty)
) { runtimeVersionCommand =>
logger.trace(
s"Executing a command that gets the runtime version: " +
s"$runtimeVersionCommand"
)
runtimeVersionCommand.captureOutput().get
}

View File

@ -24,7 +24,7 @@ case class GlobalCLIOptions(
hideProgress: Boolean,
useJSON: Boolean,
colorMode: ColorMode,
internalOptions: InternalOptions = InternalOptions(None, None)
internalOptions: InternalOptions
)
object GlobalCLIOptions {
@ -38,9 +38,13 @@ object GlobalCLIOptions {
*/
case class InternalOptions(
launcherLogLevel: Option[LogLevel],
loggerConnectUri: Option[Uri]
loggerConnectUri: Option[Uri],
logMaskingDisabled: Boolean
) {
/** @return `true` if log masking is enabled. */
def logMasking: Boolean = !logMaskingDisabled
/** Creates command line options that can be passed to a launcher process to
* set the same options.
*/
@ -51,12 +55,15 @@ object GlobalCLIOptions {
val uri = loggerConnectUri
.map(uri => Seq(s"--$CONNECT_LOGGER", uri.toString))
.getOrElse(Seq())
level ++ uri
val noMasking =
if (logMaskingDisabled) Seq(s"--$NO_LOG_MASKING") else Seq()
level ++ uri ++ noMasking
}
}
val LOG_LEVEL = "launcher-log-level"
val CONNECT_LOGGER = "internal-connect-logger"
val NO_LOG_MASKING = "no-log-masking"
/** Converts the [[GlobalCLIOptions]] to a sequence of arguments that can be
* added to a launcher invocation to set the same options.

View File

@ -508,6 +508,13 @@ object LauncherApplication {
"connects to the logging service at the provided URI."
)
.hidden
val noLogMasking = Opts.flag(
GlobalCLIOptions.NO_LOG_MASKING,
"Disable masking of personally identifiable information in logs. " +
"Masking can be also disabled with the `NO_LOG_MASKING` environment " +
"variable.",
showInUsage = false
)
val colorMode =
Opts
.aliasedOptionalParameter[ColorMode](
@ -531,6 +538,7 @@ object LauncherApplication {
hideProgress,
logLevel,
connectLogger,
noLogMasking,
colorMode
) mapN {
(
@ -542,6 +550,7 @@ object LauncherApplication {
hideProgress,
logLevel,
connectLogger,
disableLogMasking,
colorMode
) => () =>
if (shouldEnsurePortable) {
@ -553,8 +562,11 @@ object LauncherApplication {
hideProgress = hideProgress,
useJSON = useJSON,
colorMode = colorMode,
internalOptions =
GlobalCLIOptions.InternalOptions(logLevel, connectLogger)
internalOptions = GlobalCLIOptions.InternalOptions(
logLevel,
connectLogger,
disableLogMasking
)
)
internalOptsCallback(globalCLIOptions)
@ -562,7 +574,8 @@ object LauncherApplication {
LauncherLogging.setup(
logLevel,
connectLogger,
globalCLIOptions.colorMode
globalCLIOptions.colorMode,
!disableLogMasking
)
initializeApp()

View File

@ -42,6 +42,7 @@ class LauncherRunner(
projectPath: Option[Path],
versionOverride: Option[SemVer],
logLevel: LogLevel,
logMasking: Boolean,
additionalArguments: Seq[String]
): Try[RunSettings] =
Try {
@ -68,7 +69,7 @@ class LauncherRunner(
}
RunSettings(
version,
arguments ++ setLogLevelArgs(logLevel)
arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments,
connectLoggerIfAvailable = true
)
@ -82,6 +83,7 @@ class LauncherRunner(
path: Option[Path],
versionOverride: Option[SemVer],
logLevel: LogLevel,
logMasking: Boolean,
additionalArguments: Seq[String]
): Try[RunSettings] =
Try {
@ -128,14 +130,18 @@ class LauncherRunner(
}
RunSettings(
version,
arguments ++ setLogLevelArgs(logLevel)
arguments ++ setLogLevelArgs(logLevel, logMasking)
++ additionalArguments,
connectLoggerIfAvailable = true
)
}
private def setLogLevelArgs(level: LogLevel): Seq[String] =
Seq("--log-level", level.name)
private def setLogLevelArgs(
level: LogLevel,
logMasking: Boolean
): Seq[String] =
Seq("--log-level", level.name) ++
Option.unless(logMasking)("--no-log-masking")
/** Creates [[RunSettings]] for launching the Language Server.
*

View File

@ -169,7 +169,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
projectPath = None,
versionOverride = None,
additionalArguments = Seq("arg", "--flag"),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -194,7 +195,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
projectPath = Some(projectPath),
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -208,7 +210,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
projectPath = None,
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -222,7 +225,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
projectPath = Some(projectPath),
versionOverride = Some(overridden),
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -290,7 +294,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = Some(projectPath),
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -304,7 +309,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = None,
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -318,7 +324,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = Some(projectPath),
versionOverride = Some(overridden),
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -332,7 +339,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = None,
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.isFailure,
"Running outside project without providing any paths should be an error"
@ -358,7 +366,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = Some(outsideFile),
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get
@ -382,7 +391,8 @@ class LauncherRunnerSpec extends RuntimeVersionManagerTest {
path = Some(insideFile),
versionOverride = None,
additionalArguments = Seq(),
logLevel = LogLevel.Info
logLevel = LogLevel.Info,
logMasking = true
)
.get

View File

@ -39,6 +39,7 @@ object Main {
private val JSON_OPTION = "json"
private val LOG_LEVEL = "log-level"
private val LOGGER_CONNECT = "logger-connect"
private val NO_LOG_MASKING = "no-log-masking"
/** Builds the [[Options]] object representing the CLI syntax.
*
@ -175,6 +176,14 @@ object Main {
.longOpt(LOGGER_CONNECT)
.desc("Connects to a logging service server and passes all logs to it.")
.build
val noLogMaskingOption: CliOption = CliOption.builder
.longOpt(NO_LOG_MASKING)
.desc(
"Disable masking of personally identifiable information in logs. " +
"Masking can be also disabled with the `NO_LOG_MASKING` environment " +
"variable."
)
.build()
val options = new Options
options
@ -197,6 +206,7 @@ object Main {
.addOption(json)
.addOption(logLevelOption)
.addOption(loggerConnectOption)
.addOption(noLogMaskingOption)
options
}
@ -545,7 +555,8 @@ object Main {
.getOrElse(defaultLogLevel)
val connectionUri =
Option(line.getOptionValue(LOGGER_CONNECT)).map(parseUri)
RunnerLogging.setup(connectionUri, logLevel)
val logMasking = !line.hasOption(NO_LOG_MASKING)
RunnerLogging.setup(connectionUri, logLevel, logMasking)
if (line.hasOption(NEW_OPTION)) {
createNew(

View File

@ -2,6 +2,7 @@ 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}
@ -20,9 +21,15 @@ object RunnerLogging {
*
* @param connectionUri optional uri of logging service server to connect to
* @param logLevel log level to use for the runner and runtime
* @param logMasking switches log masking on and off
*/
def setup(connectionUri: Option[Uri], logLevel: LogLevel): Unit = {
def setup(
connectionUri: Option[Uri],
logLevel: LogLevel,
logMasking: Boolean
): Unit = {
import scala.concurrent.ExecutionContext.Implicits.global
Masking.setup(logMasking)
val loggerSetup = connectionUri match {
case Some(uri) =>
LoggingServiceManager
@ -31,7 +38,7 @@ object RunnerLogging {
logLevel
)
.map { _ =>
logger.trace(s"Connected to logging service at `$uri`.")
logger.trace("Connected to logging service at [{}].", uri)
}
.recoverWith { _ =>
logger.error(

View File

@ -23,6 +23,7 @@ import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.tag.IdentifiedTag;
import org.enso.interpreter.runtime.type.Types;
import org.enso.logger.masking.MaskedString;
import org.enso.pkg.QualifiedName;
import java.util.*;
@ -135,7 +136,7 @@ public class IdExecutionInstrument extends TruffleInstrument {
+ "expressionId="
+ expressionId
+ ", value="
+ value
+ new MaskedString(value.toString()).applyMasking()
+ ", type='"
+ type
+ '\''

View File

@ -1,5 +1,8 @@
package org.enso.interpreter.service.error;
import org.enso.logger.masking.MaskedPath;
import org.enso.logger.masking.MaskedString;
import java.io.File;
/** Thrown when the edits can not be applied to the source. */
@ -16,13 +19,13 @@ public class FailedToApplyEditsException extends RuntimeException implements Ser
public FailedToApplyEditsException(File path, Object edits, Object failure, Object source) {
super(
"Filed to apply edits for file "
+ path
+ " edits="
+ edits
+ " failure="
+ new MaskedPath(path.toPath()).applyMasking()
+ ", edits="
+ new MaskedString(edits.toString()).applyMasking()
+ ", failure="
+ failure
+ " source='"
+ source
+ ", source='"
+ new MaskedString(source.toString()).applyMasking()
+ "'");
}
}

View File

@ -1,5 +1,7 @@
package org.enso.interpreter.service.error;
import org.enso.logger.masking.MaskedPath;
import java.io.File;
/** Thrown when a module for given file was requested but could not be found. */
@ -11,6 +13,6 @@ public class ModuleNotFoundForFileException extends ModuleNotFoundException {
* @param path the path of the module file.
*/
public ModuleNotFoundForFileException(File path) {
super("Module not found for file " + path + ".");
super("Module not found for file " + new MaskedPath(path.toPath()).applyMasking() + ".");
}
}

View File

@ -119,7 +119,7 @@ class EnsureCompiledJob(protected val files: Iterable[File])
compile(module) match {
case Left(err) =>
ctx.executionService.getLogger
.log(Level.SEVERE, s"Compilation error in ${module.getPath}", err)
.log(Level.SEVERE, s"Compilation error in ${module.getName}", err)
sendFailureUpdate(
Api.ExecutionResult.Failure(
err.getMessage,

View File

@ -1,8 +1,9 @@
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.{Logger => SLF4JLogger, Marker}
import org.slf4j.{Marker, Logger => SLF4JLogger}
import scala.annotation.unused
@ -11,8 +12,13 @@ import scala.annotation.unused
*
* @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) extends SLF4JLogger {
class Logger(
name: String,
connection: LoggerConnection,
masking: Masking
) extends SLF4JLogger {
override def getName: String = name
private def isEnabled(level: LogLevel): Boolean =
@ -33,7 +39,8 @@ class Logger(name: String, connection: LoggerConnection) extends SLF4JLogger {
arg: AnyRef
): Unit = {
if (isEnabled(level)) {
val fp = MessageFormatter.format(format, arg)
val maskedArg = masking.mask(arg)
val fp = MessageFormatter.format(format, maskedArg)
connection.send(
InternalLogMessage(level, name, fp.getMessage, Option(fp.getThrowable))
)
@ -47,7 +54,9 @@ class Logger(name: String, connection: LoggerConnection) extends SLF4JLogger {
arg2: AnyRef
): Unit = {
if (isEnabled(level)) {
val fp = MessageFormatter.format(format, arg1, arg2)
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))
)
@ -60,7 +69,8 @@ class Logger(name: String, connection: LoggerConnection) extends SLF4JLogger {
args: Seq[AnyRef]
): Unit = {
if (isEnabled(level)) {
val fp = MessageFormatter.arrayFormat(format, args.toArray)
val maskedArgs = args.map(masking.mask)
val fp = MessageFormatter.arrayFormat(format, maskedArgs.toArray)
connection.send(
InternalLogMessage(level, name, fp.getMessage, Option(fp.getThrowable))
)

View File

@ -1,19 +1,22 @@
package org.enso.loggingservice
import org.enso.logger.masking.Masking
import org.slf4j.{ILoggerFactory, Logger => SLF4JLogger}
/** A [[ILoggerFactory]] instance for the SLF4J backend.
*/
/** A [[ILoggerFactory]] instance for the SLF4J backend. */
class LoggerFactory extends ILoggerFactory {
/** @inheritdoc
*/
override def getLogger(name: String): SLF4JLogger = {
loggers.getOrElseUpdate(
name,
new Logger(name, LoggingServiceManager.Connection)
)
}
private val loggers = scala.collection.concurrent.TrieMap[String, Logger]()
/** @inheritdoc */
override def getLogger(name: String): SLF4JLogger = {
val logger = loggers.getOrElseUpdate(
name,
new Logger(name, LoggingServiceManager.Connection, Masking())
)
if (!Masking.isMaskingEnabled) {
logger.warn("Log masking is disabled!")
}
logger
}
}

View File

@ -4,6 +4,7 @@ import java.nio.file.Path
import akka.http.scaladsl.model.Uri
import com.typesafe.scalalogging.Logger
import org.enso.logger.masking.Masking
import org.enso.loggingservice.printers.{
FileOutputPrinter,
Printer,
@ -47,13 +48,16 @@ abstract class LoggingServiceSetupHelper(implicit
* 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
colorMode: ColorMode,
logMasking: Boolean
): Unit = {
val actualLogLevel = logLevel.getOrElse(defaultLogLevel)
Masking.setup(logMasking)
connectToExternalLogger match {
case Some(uri) =>
setupLoggingConnection(uri, actualLogLevel)

View File

@ -0,0 +1,24 @@
package org.enso.logger.masking
import java.nio.file.Path
/** A path that is masked when logged.
*
* @param value the underlying path.
*/
case class MaskedPath(value: Path) extends ToLogString {
/** @inheritdoc */
override def toString: String =
value.toAbsolutePath.normalize().toString
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String = {
val path = value.toAbsolutePath.normalize()
if (shouldMask) {
MaskingUtils.toMaskedPath(path)
} else {
path.toString
}
}
}

View File

@ -0,0 +1,16 @@
package org.enso.logger.masking
/** A string that is masked when logged.
*
* @param value the underlying string.
*/
case class MaskedString(value: String) extends ToLogString {
/** @inheritdoc */
override def toString: String =
value
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String =
if (shouldMask) STUB else value
}

View File

@ -0,0 +1,55 @@
package org.enso.logger.masking
import scala.util.control.NonFatal
trait Masking {
/** Converts the provided object to a masked representation.
*
* @param obj the object to mask
* @return the masked object
*/
def mask(obj: AnyRef): AnyRef
}
object Masking {
@volatile private var maskingEnabled: Boolean = true
/** Environment variable name that disables log masking. */
val NO_LOG_MASKING = "NO_LOG_MASKING"
/** Setup the log masking.
*
* @param enabled if the log masking is enabled
*/
def setup(enabled: Boolean): Unit = {
maskingEnabled = enabled
}
/** Checks if the log masking is enabled. */
def isMaskingEnabled: Boolean = {
!sys.env.contains(NO_LOG_MASKING) &&
maskingEnabled
}
private def masking(shouldMask: Boolean): Masking = {
case obj: ToLogString =>
try obj.toLogString(shouldMask)
catch {
case NonFatal(error) =>
System.err.println(
"[internal-logger-error] " +
"Failed `toMaskedString` invocation on object of type " +
s"'${obj.getClass.getName}'. " +
s"${error.getClass.getName}: " +
s"${error.getMessage}"
)
"[Failed `toMaskedString`]"
}
case obj => obj
}
/** Get the instance of [[Masking]] adapter. */
def apply(): Masking = masking(isMaskingEnabled)
}

View File

@ -0,0 +1,27 @@
package org.enso.logger.masking
import java.nio.file.{FileSystems, Path}
object MaskingUtils {
/** A substitution for the masked data. */
final val STUB: String = "***"
/** A platform-specific file separator string. */
final val fileSeparator: String = FileSystems.getDefault.getSeparator
/** Mask a path to the file
*
* @param path the file to mask
* @return a string with a path to the file masked
*/
def toMaskedPath(path: Path): String = {
val segmentsCount = path.getNameCount
if (segmentsCount > 1) {
s"$STUB$fileSeparator${path.getFileName}"
} else {
STUB
}
}
}

View File

@ -0,0 +1,46 @@
package org.enso.logger.masking
/** Indicates that an object has a custom string representation masking some
* personally identifiable information.
*
* == Logging ==
* This object should be supplied to the logger as an argument of the template
* string. This way the logger will use the `toLogString` representation of
* the object.
*
* {{{
* log.debug("Created [{}].", obj)
* }}}
*
* == Errors ==
* Note that the string interpolation `s"Created [$obj]"` still uses the
* default `toString` implementation. When creating errors, you should use
* the `applyMasking()` function that returns the masked representation
* depending on whether or not the masking is enabled in the application.
*
* {{{
* throw new Exception(s"Failed to initialize [${obj.applyMasking}].")
* }}}
*/
trait ToLogString {
/** A substitution for the masked data. */
final protected val STUB: String = MaskingUtils.STUB
/** A synonym for the `STUB`. */
final protected val *** = STUB
/** String representation of this object with masked personally identifiable
* information.
*
* @param shouldMask decides whether or not the value should be masked
*/
def toLogString(shouldMask: Boolean): String
/** Returns the string representation defined by the `toMaskedString` method,
* based on the current masking settings.
*/
def applyMasking(): String =
Masking().mask(this).toString
}

View File

@ -0,0 +1,25 @@
package org.enso.logger.masking
import java.nio.file.Path
import org.enso.logger.masking.MaskingUtils.{fileSeparator, toMaskedPath, STUB}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class MaskingUtilsSpec extends AnyWordSpec with Matchers {
"MaskingUtils" should {
"mask a path" in {
toMaskedPath(Path.of("/")) shouldEqual STUB
toMaskedPath(Path.of("/foo")) shouldEqual STUB
toMaskedPath(Path.of("/foo/bar")) shouldEqual masked("bar")
toMaskedPath(Path.of("/foo/bar.jar")) shouldEqual masked("bar.jar")
toMaskedPath(Path.of("/foo/bar/baz")) shouldEqual masked("baz")
toMaskedPath(Path.of("/foo/bar/baz.xyz")) shouldEqual masked("baz.xyz")
}
}
private def masked(suffix: String): String =
s"$STUB$fileSeparator$suffix"
}

View File

@ -9,6 +9,7 @@ object Cli {
val JSON_OPTION = "json"
val HELP_OPTION = "help"
val NO_LOG_MASKING = "no-log-masking"
val VERBOSE_OPTION = "verbose"
val VERSION_OPTION = "version"
@ -23,7 +24,7 @@ object Cli {
val verbose: cli.Option = cli.Option
.builder("v")
.longOpt(VERBOSE_OPTION)
.desc("Increase logs verbosity. Can be added multiple times (-vv)")
.desc("Increase logs verbosity. Can be added multiple times (-vv).")
.build()
val version: cli.Option = cli.Option.builder
@ -35,6 +36,15 @@ object Cli {
.longOpt(JSON_OPTION)
.desc("Switches the --version option to JSON output.")
.build()
val noLogMasking: cli.Option = cli.Option.builder
.longOpt(NO_LOG_MASKING)
.desc(
"Disable masking of personally identifiable information in logs. " +
"Masking can be also disabled with the `NO_LOG_MASKING` environment " +
"variable."
)
.build()
}
val options: cli.Options =
@ -43,6 +53,7 @@ object Cli {
.addOption(option.verbose)
.addOption(option.version)
.addOption(option.json)
.addOption(option.noLogMasking)
/** Parse the command line options. */
def parse(args: Array[String]): Either[String, cli.CommandLine] = {

View File

@ -45,7 +45,7 @@ object ProjectManager extends App with LazyLogging {
val computeExecutionContext: ExecutionContextExecutor =
ExecutionContext.fromExecutor(
computeThreadPool,
th => logger.error("An expected error occurred", th)
th => logger.error("An expected error occurred.", th)
)
/** ZIO runtime.
@ -76,7 +76,7 @@ object ProjectManager extends App with LazyLogging {
.foldM(
failure = th =>
effectTotal {
logger.error("An error occurred during killing lang servers", th)
logger.error("An error occurred during killing lang servers.", th)
},
success = ZIO.succeed(_)
)
@ -90,7 +90,7 @@ object ProjectManager extends App with LazyLogging {
failure = th =>
effectTotal {
logger
.error("An error occurred during waiting for shutdown hooks", th)
.error("An error occurred during waiting for shutdown hooks.", th)
},
success = ZIO.succeed(_)
)
@ -117,8 +117,9 @@ object ProjectManager extends App with LazyLogging {
displayVersion(options.hasOption(Cli.JSON_OPTION))
} else {
val verbosity = options.getOptions.count(_ == Cli.option.verbose)
val logMasking = !options.hasOption(Cli.NO_LOG_MASKING)
logger.info("Starting Project Manager...")
setupLogging(verbosity) *>
setupLogging(verbosity, logMasking) *>
mainProcess.fold(
th => {
logger.error("Main process execution failed.", th)
@ -129,7 +130,10 @@ object ProjectManager extends App with LazyLogging {
}
}
private def setupLogging(verbosityLevel: Int): ZIO[Console, Nothing, Unit] = {
private def setupLogging(
verbosityLevel: Int,
logMasking: Boolean
): ZIO[Console, Nothing, Unit] = {
val level = verbosityLevel match {
case 0 => LogLevel.Info
case 1 => LogLevel.Debug
@ -142,7 +146,7 @@ object ProjectManager extends App with LazyLogging {
ZIO
.effect {
Logging.setup(Some(level), None, colorMode)
Logging.setup(Some(level), None, colorMode, logMasking)
}
.catchAll { exception =>
putStrLnErr(s"Failed to setup the logger: $exception")
@ -164,8 +168,9 @@ object ProjectManager extends App with LazyLogging {
private def logServerStartup(): UIO[Unit] =
effectTotal {
logger.info(
s"Started server at ${config.server.host}:${config.server.port}, " +
s"press enter to kill server"
"Started server at {}:{}, press enter to kill server",
config.server.host,
config.server.port
)
}

View File

@ -96,7 +96,8 @@ object ExecutorWithUnlimitedPool extends LanguageServerExecutor {
.get
runner.withCommand(runSettings, descriptor.jvmSettings) { command =>
Logger[ExecutorWithUnlimitedPool.type].trace(
s"Starting Language Server Process: $command"
"Starting Language Server Process [{}]",
command
)
val process = {

View File

@ -65,7 +65,7 @@ class HeartbeatSession(
private def pingStage: Receive = {
case WebSocketConnected =>
log.debug(s"Sending ping message to $socket")
log.debug("Sending ping message to {}.", socket)
connection.send(s"""
|{
| "jsonrpc": "2.0",
@ -78,7 +78,11 @@ class HeartbeatSession(
context.become(pongStage(cancellable))
case WebSocketStreamFailure(th) =>
logError(th, s"An error occurred during connecting to websocket $socket")
logError(
th,
"An error occurred during connecting to websocket {}.",
socket
)
context.parent ! ServerUnresponsive
stop()
@ -93,11 +97,11 @@ class HeartbeatSession(
maybeJson match {
case Left(error) =>
logError(error, "An error occurred during parsing pong reply")
logError(error, "An error occurred during parsing pong reply.")
case Right(id) =>
if (id == requestId.toString) {
log.debug(s"Received correct pong message from $socket")
log.debug("Received correct pong message from {}.", socket)
if (sendConfirmations) {
context.parent ! HeartbeatReceived
@ -106,12 +110,12 @@ class HeartbeatSession(
cancellable.cancel()
stop()
} else {
log.warning(s"Received unknown response $payload")
log.warning("Received unknown response {}.", payload)
}
}
case HeartbeatTimeout =>
log.debug(s"Heartbeat timeout detected for $requestId")
log.debug("Heartbeat timeout detected for {}.", requestId)
context.parent ! ServerUnresponsive
stop()
@ -120,7 +124,7 @@ class HeartbeatSession(
context.stop(self)
case WebSocketStreamFailure(th) =>
logError(th, s"An error occurred during waiting for Pong message")
logError(th, "An error occurred during waiting for Pong message.")
context.parent ! ServerUnresponsive
cancellable.cancel()
stop()
@ -136,12 +140,12 @@ class HeartbeatSession(
cancellable.cancel()
case WebSocketStreamFailure(th) =>
logError(th, s"An error occurred during closing web socket")
logError(th, "An error occurred during closing web socket.")
context.stop(self)
cancellable.cancel()
case SocketClosureTimeout =>
logError(s"Socket closure timed out")
logError("Socket closure timed out.")
context.stop(self)
connection.detachListener(self)
@ -155,6 +159,18 @@ class HeartbeatSession(
context.become(socketClosureStage(closureTimeout))
}
private def logError(
throwable: Throwable,
message: String,
arg: AnyRef
): Unit = {
if (quietErrors) {
log.debug(s"$message ($throwable)", arg)
} else {
log.error(throwable, message, arg)
}
}
private def logError(throwable: Throwable, message: String): Unit = {
if (quietErrors) {
log.debug(s"$message ($throwable)")

View File

@ -50,7 +50,7 @@ class LanguageServerBootLoader(
import context.dispatcher
override def preStart(): Unit = {
log.info(s"Booting a language server [$descriptor]")
log.info("Booting a language server [{}].", descriptor)
self ! FindFreeSocket
}
@ -62,16 +62,19 @@ class LanguageServerBootLoader(
*/
private def findingSocket(retry: Int = 0): Receive = {
case FindFreeSocket =>
log.debug("Looking for available socket to bind the language server")
log.debug("Looking for available socket to bind the language server.")
val jsonRpcPort = findPort()
var binaryPort = findPort()
while (binaryPort == jsonRpcPort) {
binaryPort = findPort()
}
log.info(
s"Found sockets for the language server " +
s"[json:${descriptor.networkConfig.interface}:$jsonRpcPort, " +
s"binary:${descriptor.networkConfig.interface}:$binaryPort]"
"Found sockets for the language server " +
"[json:{}:{}, binary:{}:{}].",
descriptor.networkConfig.interface,
jsonRpcPort,
descriptor.networkConfig.interface,
binaryPort
)
self ! Boot
context.become(
@ -117,7 +120,7 @@ class LanguageServerBootLoader(
bootRequester: ActorRef
): Receive = {
case Boot =>
log.debug("Booting a language server")
log.debug("Booting a language server.")
context.actorOf(
LanguageServerProcess.props(
progressTracker = bootProgressTracker,
@ -155,7 +158,7 @@ class LanguageServerBootLoader(
rpcPort = rpcPort,
dataPort = dataPort
)
log.info(s"Language server booted [$connectionInfo].")
log.info("Language server booted [{}].", connectionInfo)
bootRequester ! ServerBooted(connectionInfo, self)
context.become(running(connectionInfo))
@ -183,7 +186,8 @@ class LanguageServerBootLoader(
} else {
if (shouldRetry) {
log.error(
s"Tried $retryCount times to boot Language Server. Giving up."
"Tried {} times to boot Language Server. Giving up.",
retryCount
)
} else {
log.error("Failed to restart the server. Giving up.")
@ -204,7 +208,8 @@ class LanguageServerBootLoader(
private def running(connectionInfo: LanguageServerConnectionInfo): Receive = {
case msg @ LanguageServerProcess.ServerTerminated(exitCode) =>
log.debug(
s"Language Server process has terminated with exit code $exitCode"
"Language Server process has terminated with exit code {}.",
exitCode
)
context.parent ! msg
context.stop(self)
@ -230,8 +235,9 @@ class LanguageServerBootLoader(
): Receive = {
case LanguageServerProcess.ServerTerminated(exitCode) =>
log.debug(
s"Language Server process has terminated (as requested to reboot) " +
s"with exit code $exitCode"
"Language Server process has terminated (as requested to reboot) " +
"with exit code {}.",
exitCode
)
context.become(rebooting(connectionInfo, rebootRequester))

View File

@ -115,12 +115,12 @@ class LanguageServerController(
private def booting(Bootloader: ActorRef): Receive = {
case BootTimeout =>
log.error(s"Booting failed for $descriptor")
log.error("Booting failed for {}.", descriptor)
unstashAll()
context.become(bootFailed(LanguageServerProtocol.ServerBootTimedOut))
case ServerBootFailed(th) =>
log.error(th, s"Booting failed for $descriptor")
log.error(th, "Booting failed for {}.", descriptor)
unstashAll()
context.become(bootFailed(LanguageServerProtocol.ServerBootFailed(th)))
@ -139,12 +139,12 @@ class LanguageServerController(
)
case Terminated(Bootloader) =>
log.error(s"Bootloader for project ${project.name} failed")
log.error("Bootloader for project {} failed.", project.name)
unstashAll()
context.become(
bootFailed(
LanguageServerProtocol.ServerBootFailed(
new Exception("The number of boot retries exceeded")
new Exception("The number of boot retries exceeded.")
)
)
)
@ -179,7 +179,7 @@ class LanguageServerController(
)
}
case Terminated(_) =>
log.debug(s"Bootloader for $project terminated.")
log.debug("Bootloader for {} terminated.", project)
case StopServer(clientId, _) =>
removeClient(
@ -219,7 +219,7 @@ class LanguageServerController(
)
case ServerDied =>
log.error(s"Language server died [$connectionInfo]")
log.error("Language server died [{}].", connectionInfo)
context.stop(self)
}
@ -243,7 +243,7 @@ class LanguageServerController(
}
private def shutDownServer(maybeRequester: Option[ActorRef]): Unit = {
log.debug(s"Shutting down a language server for project ${project.id}")
log.debug("Shutting down a language server for project {}.", project.id)
context.children.foreach(_ ! GracefulStop)
val cancellable =
context.system.scheduler
@ -264,18 +264,19 @@ class LanguageServerController(
case LanguageServerProcess.ServerTerminated(exitCode) =>
cancellable.cancel()
if (exitCode == 0) {
log.info(s"Language server shut down successfully [$project].")
log.info("Language server shut down successfully [{}].", project)
} else {
log.warning(
s"Language server shut down with non-zero exit code: $exitCode " +
s"[$project]."
"Language server shut down with non-zero exit code: {} [{}].",
exitCode,
project
)
}
maybeRequester.foreach(_ ! ServerStopped)
stop()
case ShutdownTimeout =>
log.error("Language server shutdown timed out")
log.error("Language server shutdown timed out.")
maybeRequester.foreach(_ ! ServerShutdownTimedOut)
stop()

View File

@ -40,7 +40,7 @@ class LanguageServerKiller(
sender() ! AllServersKilled
context.stop(self)
} else {
log.info("Killing all servers")
log.info("Killing all servers [{}].", controllers)
controllers.foreach(context.watch)
controllers.foreach(_ ! ShutDownServer)
val cancellable =
@ -61,7 +61,7 @@ class LanguageServerKiller(
case Terminated(dead) =>
val updated = liveControllers - dead
if (updated.isEmpty) {
log.info("All language servers have been killed")
log.info("All language servers have been killed.")
cancellable.cancel()
replyTo ! AllServersKilled
context.stop(self)

View File

@ -82,9 +82,8 @@ class LanguageServerSupervisor(
)
case ServerUnresponsive =>
log.info(s"Server is unresponsive [$connectionInfo]. Restarting it...")
cancellable.cancel()
log.info(s"Restarting the server")
log.info("Server is unresponsive. Restarting [{}].", connectionInfo)
serverProcessManager ! Restart
context.become(restarting)
@ -95,7 +94,7 @@ class LanguageServerSupervisor(
private def restarting: Receive = {
case ServerBootFailed(_) =>
log.error("Cannot restart language server")
log.error("Cannot restart language server.")
context.parent ! ServerDied
context.stop(self)
@ -106,7 +105,7 @@ class LanguageServerSupervisor(
"Supervisor may no longer work correctly."
)
}
log.info(s"Language server restarted [$connectionInfo]")
log.info("Language server restarted [{}].", connectionInfo)
val cancellable =
scheduler.scheduleAtFixedRate(
supervisionConfig.initialDelay,

View File

@ -64,7 +64,7 @@ class ProjectRenameAction(
private var maybeActionTimeoutCancellable: Option[Cancellable] = None
override def preStart(): Unit = {
log.info(s"Requesting a Language Server to rename project $oldName")
log.info("Requesting a Language Server to rename project [{}].", oldName)
connection.attachListener(self)
connection.connect()
val cancellable =
@ -92,7 +92,11 @@ class ProjectRenameAction(
context.become(connected())
case WebSocketStreamFailure(th) =>
log.error(th, s"An error occurred during connecting to websocket $socket")
log.error(
th,
"An error occurred during connecting to websocket {}.",
socket
)
replyTo ! CannotConnectToServer
stop()
@ -102,7 +106,7 @@ class ProjectRenameAction(
stop()
case GracefulStop =>
log.warning("Ignoring stop command")
log.warning("Ignoring stop command (Language Server is not connected).")
}
private def connected(): Receive = {
@ -122,7 +126,7 @@ class ProjectRenameAction(
maybeActionTimeoutCancellable.foreach(_.cancel())
case WebSocketStreamFailure(th) =>
log.error(th, s"An error occurred during waiting for Pong message")
log.error(th, "An error occurred during waiting for Pong message.")
replyTo ! ServerUnresponsive
stop()
@ -132,7 +136,7 @@ class ProjectRenameAction(
stop()
case GracefulStop =>
log.warning("Ignoring stop command")
log.warning("Ignoring stop command (Language Server is connected).")
}
private def handleSuccess(payload: String): Unit = {
@ -141,15 +145,15 @@ class ProjectRenameAction(
maybeRequestId match {
case Left(error) =>
log.error(error, "An error occurred during parsing rename reply")
log.error(error, "An error occurred during parsing rename reply.")
case Right(id) =>
if (id == requestId.toString) {
log.info(s"Project renamed by the Language Server")
log.info("Project renamed by the Language Server.")
replyTo ! ProjectRenamed
stop()
} else {
log.warning(s"Received unknown response $payload")
log.warning("Received unknown response [{}].", payload)
}
}
}
@ -161,7 +165,9 @@ class ProjectRenameAction(
.flatMap(_.hcursor.downField("message").as[String])
.getOrElse("Not Provided")
log.error(
s"Error occurred during renaming project [code: $code message: $msg]"
"Error occurred during renaming project [code: {}, message: {}]",
code,
msg
)
replyTo ! RenameFailure(code, msg)
stop()
@ -175,16 +181,18 @@ class ProjectRenameAction(
closureTimeoutCancellable.cancel()
case WebSocketStreamFailure(th) =>
log.error(th, s"An error occurred during closing web socket")
log.error(th, "An error occurred during closing web socket.")
context.stop(self)
closureTimeoutCancellable.cancel()
case SocketClosureTimeout =>
log.error(s"Socket closure timed out")
log.error("Socket closure timed out.")
context.stop(self)
case GracefulStop =>
log.warning("Ignoring stop command")
log.warning(
"Ignoring stop command (closing connection to Language Server)."
)
}
private def stop(): Unit = {

View File

@ -30,7 +30,7 @@ class ShutdownHookRunner[F[+_, +_]: Exec: CovariantFlatMap](
}
override def receive: Receive = { case Run =>
log.info(s"Firing shutdown hooks for project with id=$projectId")
log.info("Firing shutdown hooks for project [{}].", projectId)
Exec[F].exec { traverse(hooks) { _.execute() } } pipeTo self
context.become(running)
}
@ -39,12 +39,13 @@ class ShutdownHookRunner[F[+_, +_]: Exec: CovariantFlatMap](
case Status.Failure(th) =>
log.error(
th,
s"An error occurred during running shutdown hooks for project with id=$projectId"
"An error occurred during running shutdown hooks for project [{}].",
projectId
)
context.stop(self)
case Right(_) =>
log.info(s"All shutdown hooks fired for project with id=$projectId")
log.info("All shutdown hooks fired for project [{}].", projectId)
context.stop(self)
}

View File

@ -8,8 +8,13 @@ trait Logging[F[+_, +_]] {
def debug(msg: String): F[Nothing, Unit]
def debug(msg: String, args: AnyRef*): F[Nothing, Unit]
def info(msg: String): F[Nothing, Unit]
def info(msg: String, args: AnyRef*): F[Nothing, Unit]
def error(msg: String): F[Nothing, Unit]
def error(msg: String, args: AnyRef*): F[Nothing, Unit]
}

View File

@ -2,17 +2,24 @@ package org.enso.projectmanager.infrastructure.log
import com.typesafe.scalalogging.LazyLogging
import org.enso.projectmanager.control.effect.Sync
/** Slf4j logging interpreter.
*/
/** Slf4j logging interpreter. */
class Slf4jLogging[F[+_, +_]: Sync] extends Logging[F] with LazyLogging {
override def debug(msg: String): F[Nothing, Unit] =
Sync[F].effect(logger.debug(msg))
override def debug(msg: String, args: AnyRef*): F[Nothing, Unit] =
Sync[F].effect(logger.debug(msg, args: _*))
override def info(msg: String): F[Nothing, Unit] =
Sync[F].effect(logger.info(msg))
override def info(msg: String, args: AnyRef*): F[Nothing, Unit] =
Sync[F].effect(logger.info(msg, args: _*))
override def error(msg: String): F[Nothing, Unit] =
Sync[F].effect(logger.error(msg))
override def error(msg: String, args: AnyRef*): F[Nothing, Unit] =
Sync[F].effect(logger.error(msg, args: _*))
}

View File

@ -81,7 +81,8 @@ class ProjectFileRepository[
/** @inheritdoc */
override def findPathForNewProject(
project: Project
): F[ProjectRepositoryFailure, Path] = findTargetPath(project).map(_.toPath)
): F[ProjectRepositoryFailure, Path] =
findTargetPath(project).map(_.toPath)
private def tryLoadProject(
directory: File

View File

@ -100,7 +100,7 @@ class ClientController[F[+_, +_]: Exec: CovariantFlatMap: ErrorChannel](
override def receive: Receive = {
case JsonRpcServer.WebConnect(webActor) =>
log.info(s"Client connected to Project Manager [$clientId]")
log.info("Client connected to Project Manager [{}]", clientId)
unstashAll()
context.become(connected(webActor))
context.system.eventStream.publish(ClientConnected(clientId))
@ -110,7 +110,7 @@ class ClientController[F[+_, +_]: Exec: CovariantFlatMap: ErrorChannel](
def connected(@unused webActor: ActorRef): Receive = {
case MessageHandler.Disconnected =>
log.info(s"Client disconnected from the Project Manager [$clientId]")
log.info("Client disconnected from the Project Manager [{}]", clientId)
context.system.eventStream.publish(ClientDisconnected(clientId))
context.stop(self)

View File

@ -51,7 +51,7 @@ class LoggingServiceEndpointRequestHandler(
timeoutCancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $method operation:")
log.error(ex, "Failure during {} operation.", method)
replyTo ! ResponseError(
Some(id),
LoggingServiceUnavailable(s"Logging service failed to set up: $ex")
@ -60,7 +60,7 @@ class LoggingServiceEndpointRequestHandler(
context.stop(self)
case RequestTimeout =>
log.error(s"Request $method with $id timed out")
log.error("Request {} with {} timed out.", method, id)
replyTo ! ResponseError(
Some(id),
LoggingServiceUnavailable(

View File

@ -51,18 +51,18 @@ class ProjectCloseHandler[F[+_, +_]: Exec](
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $ProjectClose operation:")
log.error(ex, "Failure during {} operation.", ProjectClose)
replyTo ! ResponseError(Some(id), ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $ProjectClose with $id timed out")
log.error("Request {} with {} timed out.", ProjectClose, id)
replyTo ! ResponseError(Some(id), ServiceError)
context.stop(self)
case Left(failure: ProjectServiceFailure) =>
log.error(s"Request $id failed due to $failure")
log.error("Request {} failed due to {}.", id, failure)
replyTo ! ResponseError(Some(id), mapFailure(failure))
cancellable.cancel()
context.stop(self)

View File

@ -45,18 +45,18 @@ class ProjectDeleteHandler[F[+_, +_]: Exec](
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $ProjectDelete operation:")
log.error(ex, "Failure during {} operation.", ProjectDelete)
replyTo ! ResponseError(Some(id), ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $ProjectDelete with $id timed out")
log.error("Request {} with {} timed out.", ProjectDelete, id)
replyTo ! ResponseError(Some(id), ServiceError)
context.stop(self)
case Left(failure: ProjectServiceFailure) =>
log.error(s"Request $id failed due to $failure")
log.error("Request {} failed due to {}.", id, failure)
replyTo ! ResponseError(Some(id), mapFailure(failure))
cancellable.cancel()
context.stop(self)

View File

@ -54,18 +54,18 @@ class ProjectListHandler[F[+_, +_]: Exec](
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $ProjectList operation:")
log.error(ex, "Failure during {} operation.", ProjectList)
replyTo ! ResponseError(Some(id), ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $ProjectList with $id timed out")
log.error("Request {} with {} timed out.", ProjectList, id)
replyTo ! ResponseError(Some(id), ServiceError)
context.stop(self)
case Left(failure: ProjectServiceFailure) =>
log.error(s"Request $id failed due to $failure")
log.error("Request {} failed due to {}.", id, failure)
replyTo ! ResponseError(Some(id), mapFailure(failure))
cancellable.cancel()
context.stop(self)

View File

@ -48,18 +48,18 @@ class ProjectRenameHandler[F[+_, +_]: Exec](
cancellable: Cancellable
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $ProjectRename operation:")
log.error(ex, "Failure during {} operation.", ProjectRename)
replyTo ! ResponseError(Some(id), ServiceError)
cancellable.cancel()
context.stop(self)
case RequestTimeout =>
log.error(s"Request $ProjectRename with $id timed out")
log.error("Request {} with {} timed out.", ProjectRename, id)
replyTo ! ResponseError(Some(id), ServiceError)
context.stop(self)
case Left(failure: ProjectServiceFailure) =>
log.error(s"Request $id failed due to $failure")
log.error(s"Request {} failed due to {}.", id, failure)
replyTo ! ResponseError(Some(id), mapFailure(failure))
cancellable.cancel()
context.stop(self)

View File

@ -90,18 +90,18 @@ abstract class RequestHandler[
timeoutCancellable: Option[Cancellable]
): Receive = {
case Status.Failure(ex) =>
log.error(ex, s"Failure during $method operation:")
log.error(ex, "Failure during {} operation.", method)
replyTo ! ResponseError(Some(id), ServiceError)
timeoutCancellable.foreach(_.cancel())
context.stop(self)
case RequestTimeout =>
log.error(s"Request $method with $id timed out")
log.error("Request {} with {} timed out.", method, id)
replyTo ! ResponseError(Some(id), ServiceError)
context.stop(self)
case Left(failure: FailureType) =>
log.error(s"Request $method with $id failed due to $failure")
log.error("Request {} with {} failed due to {}.", method, id, failure)
val error = implicitly[FailureMapper[FailureType]].mapFailure(failure)
replyTo ! ResponseError(Some(id), error)
timeoutCancellable.foreach(_.cancel())
@ -134,8 +134,10 @@ abstract class RequestHandler[
timeoutCancellable.foreach { cancellable =>
cancellable.cancel()
Logger[this.type].trace(
s"The operation $method ($id) reported starting a long-running task, " +
s"its request-timeout has been cancelled."
"The operation {} ({}) reported starting a long-running task, " +
"its request-timeout has been cancelled.",
method,
id
)
}
context.become(responseStage(id, replyTo, None))

View File

@ -31,9 +31,9 @@ class MoveProjectDirCmd[F[+_, +_]: CovariantFlatMap: ErrorChannel](
override def execute(): F[Nothing, Unit] = {
def go() =
for {
_ <- log.debug(s"Moving project ${projectId} to $newName")
_ <- log.debug("Moving project [{}] to [{}].", projectId, newName)
dir <- repo.moveProjectToTargetDir(projectId, newName)
_ <- log.info(s"Project $projectId moved to $dir")
_ <- log.info("Project [{}] moved to [{}].", projectId, dir)
} yield ()
go().fallbackTo(logError)
@ -41,7 +41,9 @@ class MoveProjectDirCmd[F[+_, +_]: CovariantFlatMap: ErrorChannel](
private def logError(failure: ProjectRepositoryFailure): F[Nothing, Unit] = {
log.error(
s"An error occurred during moving project $projectId [$failure]"
"An error occurred during moving project {} [{}].",
projectId,
failure
)
}

View File

@ -83,14 +83,17 @@ class ProjectService[
missingComponentAction: MissingComponentAction
): F[ProjectServiceFailure, UUID] = for {
projectId <- gen.randomUUID()
_ <- log.debug(s"Creating project $name $projectId.")
_ <- log.debug("Creating project [{}, {}].", name, projectId)
_ <- validateName(name)
_ <- checkIfNameExists(name)
creationTime <- clock.nowInUtc()
project = Project(projectId, name, UserProject, creationTime)
path <- repo.findPathForNewProject(project).mapError(toServiceFailure)
_ <- log.debug(
s"Found a path '$path' for a new project $name $projectId."
"Found a path [{}] for a new project [{}, {}].",
path,
name,
projectId
)
_ <- projectCreationService.createProject(
progressTracker,
@ -100,24 +103,27 @@ class ProjectService[
missingComponentAction
)
_ <- log.debug(
s"Project $projectId structure created with " +
s"$path, $name, $engineVersion."
"Project [{}] structure created with [{}, {}, {}].",
projectId,
path,
name,
engineVersion
)
_ <- repo
.update(project.copy(path = Some(path.toString)))
.mapError(toServiceFailure)
_ <- log.debug(s"Project $projectId updated in repository $repo.")
_ <- log.info(s"Project $project created.")
_ <- log.debug("Project [{}] updated in repository [{}].", projectId, repo)
_ <- log.info("Project created [{}].", project)
} yield projectId
/** @inheritdoc */
override def deleteUserProject(
projectId: UUID
): F[ProjectServiceFailure, Unit] =
log.debug(s"Deleting project $projectId.") *>
log.debug("Deleting project [{}].", projectId) *>
ensureProjectIsNotRunning(projectId) *>
repo.delete(projectId).mapError(toServiceFailure) *>
log.info(s"Project $projectId deleted.")
log.info("Project deleted [{}].", projectId)
private def ensureProjectIsNotRunning(
projectId: UUID
@ -141,7 +147,7 @@ class ProjectService[
newPackage: String
): F[ProjectServiceFailure, Unit] = {
for {
_ <- log.debug(s"Renaming project $projectId to $newPackage.")
_ <- log.debug("Renaming project [{}] to [{}].", projectId, newPackage)
_ <- validateName(newPackage)
_ <- checkIfProjectExists(projectId)
_ <- checkIfNameExists(newPackage)
@ -149,7 +155,7 @@ class ProjectService[
_ <- repo.rename(projectId, newPackage).mapError(toServiceFailure)
_ <- renameProjectDirOrRegisterShutdownHook(projectId, newPackage)
_ <- refactorProjectName(projectId, oldPackage, newPackage)
_ <- log.info(s"Project $projectId renamed.")
_ <- log.info("Project renamed [{}].", projectId)
} yield ()
}
@ -162,15 +168,19 @@ class ProjectService[
.ifM(isServerRunning(projectId))(
ifTrue = for {
_ <- log.debug(
s"Registering shutdown hook to rename the project $projectId " +
s"with a new name '$newName''"
"Registering shutdown hook to rename the project [{}] " +
"with a new name [{}].",
projectId,
newName
)
_ <- languageServerGateway.registerShutdownHook(projectId, cmd)
} yield (),
ifFalse = for {
_ <- log.debug(
s"Running a command to rename the project $projectId " +
s"with a new name '$newName"
"Running a command to rename the project [{}] " +
"with a new name [{}].",
projectId,
newName
)
_ <- cmd.execute()
} yield ()
@ -195,20 +205,22 @@ class ProjectService[
case ProjectNotOpened => ProjectNotOpen //impossible
case RenameTimeout => ProjectOperationTimeout
case CannotConnectToServer =>
LanguageServerFailure("Cannot connect to the language server")
LanguageServerFailure("Cannot connect to the language server.")
case RenameFailure(code, msg) =>
LanguageServerFailure(
s"Failure during renaming [code: $code message: $msg]"
s"Failure during renaming [code: $code message: $msg]."
)
case ServerUnresponsive =>
LanguageServerFailure("The language server is unresponsive")
LanguageServerFailure("The language server is unresponsive.")
}
.flatMap { _ =>
log.debug(
s"Language Server replied to the project $projectId rename command " +
s"from $oldPackage to $newPackage."
"Language Server replied to the project [{}] rename command from {} to {}.",
projectId,
oldPackage,
newPackage
)
}
@ -224,8 +236,9 @@ class ProjectService[
}
.flatMap { _ =>
log.debug(
s"Checked if the project $projectId exists " +
s"in repo $repo."
"Checked if the project [{}] exists in repo [{}].",
projectId,
repo
)
}
@ -238,7 +251,7 @@ class ProjectService[
): F[ProjectServiceFailure, RunningLanguageServerInfo] = {
// format: off
for {
_ <- log.debug(s"Opening project $projectId")
_ <- log.debug(s"Opening project [{}].", projectId)
project <- getUserProject(projectId)
openTime <- clock.nowInUtc()
updated = project.copy(lastOpened = Some(openTime))
@ -262,7 +275,7 @@ class ProjectService[
}
.mapRuntimeManagerErrors(th =>
ProjectOpenFailed(
s"Cannot install the required engine ${th.getMessage}"
s"Cannot install the required engine. ${th.getMessage}"
)
)
@ -276,8 +289,8 @@ class ProjectService[
.resolveEnsoVersion(project.engineVersion)
.mapError { case ConfigurationFileAccessFailure(message) =>
ProjectOpenFailed(
s"Could not deduce the default version to use for the project: " +
s"$message"
"Could not deduce the default version to use for the project: " +
message
)
}
_ <- preinstallEngine(progressTracker, version, missingComponentAction)
@ -291,11 +304,11 @@ class ProjectService[
)
case ServerBootTimedOut =>
ProjectOpenFailed("Language server boot timed out")
ProjectOpenFailed("Language server boot timed out.")
case ServerBootFailed(th) =>
ProjectOpenFailed(
s"Language server boot failed: ${th.getMessage}"
s"Language server boot failed. ${th.getMessage}"
)
}
} yield RunningLanguageServerInfo(version, sockets)
@ -305,10 +318,10 @@ class ProjectService[
clientId: UUID,
projectId: UUID
): F[ProjectServiceFailure, Unit] = {
log.debug(s"Closing project $projectId") *>
log.debug(s"Closing project [{}].", projectId) *>
languageServerGateway.stop(clientId, projectId).mapError {
case ServerShutdownTimedOut =>
ProjectCloseFailed("Server shutdown timed out")
ProjectCloseFailed("Server shutdown timed out.")
case FailureDuringShutdown(th) => ProjectCloseFailed(th.getMessage)
case ServerNotRunning => ProjectNotOpen
@ -336,8 +349,8 @@ class ProjectService[
.resolveEnsoVersion(project.engineVersion)
.mapError { case ConfigurationFileAccessFailure(message) =>
GlobalConfigurationAccessFailure(
s"Could not deduce the default version to use for the project: " +
s"$message"
"Could not deduce the default version to use for the project: " +
message
)
}
.map(toProjectMetadata(_, project))
@ -365,7 +378,7 @@ class ProjectService[
}
.flatMap { project =>
log
.debug(s"Found project $projectId in the $repo.")
.debug("Found project [{}] in [{}].", projectId, repo)
.map(_ => project)
}
@ -381,21 +394,22 @@ class ProjectService[
}
.flatMap { _ =>
log.debug(
s"Checked if the project name '$name' exists " +
s"in the $repo."
"Checked if the project name [{}] exists in [{}].",
name,
repo
)
}
private val toServiceFailure
: ProjectRepositoryFailure => ProjectServiceFailure = {
case CannotLoadIndex(msg) =>
DataStoreFailure(s"Cannot load project index [$msg]")
DataStoreFailure(s"Cannot load project index [$msg].")
case StorageFailure(msg) =>
DataStoreFailure(s"Storage failure [$msg]")
DataStoreFailure(s"Storage failure [$msg].")
case ProjectNotFoundInIndex =>
ProjectNotFound
case InconsistentStorage(msg) =>
DataStoreFailure(s"Project repository inconsistency detected [$msg]")
DataStoreFailure(s"Project repository inconsistency detected [$msg].")
}
private def validateName(
@ -406,23 +420,23 @@ class ProjectService[
.mapError {
case EmptyName =>
ProjectServiceFailure.ValidationFailure(
"Project name cannot be empty"
"Project name cannot be empty."
)
case NameContainsForbiddenCharacter(chars) =>
ProjectServiceFailure.ValidationFailure(
s"Project name contains forbidden characters: ${chars.mkString(",")}"
s"Project name contains forbidden characters: [${chars.mkString(",")}]."
)
case NameShouldStartWithCapitalLetter =>
ProjectServiceFailure.ValidationFailure(
"Project name should start with a capital letter"
"Project name should start with a capital letter."
)
case NameShouldBeUpperSnakeCase(validName) =>
ProjectServiceFailure.ValidationFailure(
s"Project name should be in upper snake case: $validName"
s"Project name should be in upper snake case: $validName."
)
}
.flatMap { _ =>
log.debug(s"Project name '$name' validated by $validator.")
log.debug("Project name [{}] validated by [{}].", name, validator)
}
}

View File

@ -15,7 +15,7 @@ import org.enso.projectmanager.versionmanagement.DistributionConfiguration
import org.enso.runtimeversionmanager.components.ComponentMissingError
/** A facade for runtime version management logic that processes the requests
* using the [[RuntimeVersionManager]].
* using the [[org.enso.runtimeversionmanager.components.RuntimeVersionManager]].
*
* @param distributionConfiguration a distribution configuration
*/

View File

@ -43,7 +43,7 @@ class ProjectManagementApiSpec
client.expectJson(json"""
{ "jsonrpc": "2.0",
"id": 1,
"error": { "code": 4001, "message": "Project name cannot be empty" }
"error": { "code": 4001, "message": "Project name cannot be empty." }
}
""")
}
@ -64,7 +64,7 @@ class ProjectManagementApiSpec
"id":1,
"error":{
"code":4001,
"message":"Project name contains forbidden characters: -,/,#,$$,%,^,@,!"
"message":"Project name contains forbidden characters: [-,/,#,$$,%,^,@,!]."
}
}
""")
@ -86,7 +86,7 @@ class ProjectManagementApiSpec
"id":1,
"error":{
"code":4001,
"message":"Project name should start with a capital letter"
"message":"Project name should start with a capital letter."
}
}
""")
@ -108,7 +108,7 @@ class ProjectManagementApiSpec
"id":1,
"error":{
"code":4001,
"message":"Project name should be in upper snake case: Enso_Test_Project"
"message":"Project name should be in upper snake case: Enso_Test_Project."
}
}
""")
@ -872,7 +872,7 @@ class ProjectManagementApiSpec
client.expectJson(json"""
{ "jsonrpc": "2.0",
"id": 0,
"error": { "code": 4001, "message": "Project name cannot be empty" }
"error": { "code": 4001, "message": "Project name cannot be empty." }
}
""")
//teardown
@ -900,7 +900,7 @@ class ProjectManagementApiSpec
"id":0,
"error":{
"code":4001,
"message":"Project name contains forbidden characters: -,/,#,$$,%,^,@,!"
"message":"Project name contains forbidden characters: [-,/,#,$$,%,^,@,!]."
}
}
""")
@ -929,7 +929,7 @@ class ProjectManagementApiSpec
"id":0,
"error":{
"code":4001,
"message":"Project name should start with a capital letter"
"message":"Project name should start with a capital letter."
}
}
""")
@ -958,7 +958,7 @@ class ProjectManagementApiSpec
"id":0,
"error":{
"code":4001,
"message":"Project name should be in upper snake case: Enso_Test_Project"
"message":"Project name should be in upper snake case: Enso_Test_Project."
}
}
""")

View File

@ -4,9 +4,16 @@ import org.enso.projectmanager.infrastructure.log.Logging
import zio.{IO, ZIO}
class NopLogging[R] extends Logging[ZIO[R, +*, +*]] {
override def debug(msg: String): IO[Nothing, Unit] = IO.unit
override def debug(msg: String, args: AnyRef*): IO[Nothing, Unit] = IO.unit
override def info(msg: String): IO[Nothing, Unit] = IO.unit
override def info(msg: String, args: AnyRef*): IO[Nothing, Unit] = IO.unit
override def error(msg: String): IO[Nothing, Unit] = IO.unit
override def error(msg: String, args: AnyRef*): IO[Nothing, Unit] = IO.unit
}

View File

@ -30,7 +30,8 @@ object CurrentVersion {
"release build."
)
else {
Logger("TEST").debug(s"Overriding version to $newVersion.")
Logger[CurrentVersion.type]
.debug(s"Overriding version to [{}].", newVersion)
currentVersion = newVersion
}

Some files were not shown because too many files have changed in this diff Show More