Skip to content

Commit

Permalink
Move to scala classes from java interfaces, push for better overview …
Browse files Browse the repository at this point in the history
…of changes
  • Loading branch information
rochala committed Mar 19, 2024
1 parent 07ed784 commit 69c708f
Show file tree
Hide file tree
Showing 41 changed files with 471 additions and 1,868 deletions.
22 changes: 13 additions & 9 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ commands ++= Seq(
runMtagsPublishLocal(st, v, localSnapshotVersion)
}
"interfaces/publishLocal" ::
"telemetryInterfaces/publishLocal" ::
"+telemetryInterfaces/publishLocal" ::
s"++${V.scala213} metals/publishLocal" ::
"mtags-java/publishLocal" ::
publishMtags
Expand Down Expand Up @@ -193,8 +193,6 @@ def lintingOptions(scalaVersion: String) = {
}

val sharedJavacOptions = List(
packageDoc / publishArtifact := true,
packageSrc / publishArtifact := true,
Compile / javacOptions ++= {
if (sys.props("java.version").startsWith("1.8"))
Nil
Expand Down Expand Up @@ -257,13 +255,20 @@ lazy val interfaces = project

lazy val telemetryInterfaces = project
.in(file("telemetry-interfaces"))
.settings(sharedJavacOptions)
.settings(sharedSettings)
.settings(
moduleName := "telemetry-interfaces",
autoScalaLibrary := false,
crossVersion := CrossVersion.disabled,
crossPaths := false,
libraryDependencies += "com.google.code.gson" % "gson" % V.gson,
crossScalaVersions := List(V.scala213, V.scala3),
crossVersion := CrossVersion.binary,
Compile / scalacOptions ++= {
if (scalaVersion.value == V.scala3)
List("-Xmax-inlines", "64")
else Nil
},
libraryDependencies := List(
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.27.7",
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.27.7" % "compile-internal"
)
)

lazy val mtagsShared = project
Expand Down Expand Up @@ -697,7 +702,6 @@ def publishAllMtags(
def publishBinaryMtags =
(interfaces / publishLocal)
.dependsOn(
telemetryInterfaces / publishLocal,
`mtags-java` / publishLocal,
publishAllMtags(V.quickPublishScalaVersions),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1144,10 +1144,10 @@ class Compilers(
config: PresentationCompilerConfig,
options: List[String],
): ReporterContext =
new ScalaPresentationCompilerContext(
scalaVersion,
options.asJava,
telemetry.conversion.PresentationCompilerConfig(config),
ScalaPresentationCompilerContext(
scalaVersion = scalaVersion,
options = options,
config = telemetry.conversion.PresentationCompilerConfig(config),
)

private def remoteReporting(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2851,19 +2851,15 @@ class MetalsLspService(
def runDoctorCheck(): Unit = doctor.check(headDoctor)

private def createTelemetryReporterContext(): telemetry.ReporterContext =
new telemetry.MetalsLspContext(
/* metalsVersion = */ BuildInfo.metalsVersion,
/* userConfig = */ telemetry.conversion.UserConfiguration(userConfig),
/* serverConfig = */ telemetry.conversion.MetalsServerConfig(
telemetry.MetalsLspContext(
metalsVersion = BuildInfo.metalsVersion,
userConfig = telemetry.conversion.UserConfiguration(userConfig),
serverConfig = telemetry.conversion.MetalsServerConfig(
serverInputs.initialServerConfig
),
/* clientInfo =*/ telemetry.conversion.MetalsClientInfo(
initializeParams.getClientInfo()
),
/* buildServerConnections = */ bspSession.toList
.flatMap(
telemetry.conversion.BuildServerConnections(_)
)
.asJava,
clientInfo =
telemetry.conversion.MetalsClientInfo(initializeParams.getClientInfo()),
buildServerConnections = bspSession.toList
.flatMap(telemetry.conversion.BuildServerConnections(_)),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ object UserConfiguration {
"false",
"false",
"Should display implicit parameter at usage sites",
"""|When this option is enabled, each method that has implicit arguments has them
|displayed either as additional decorations if they are supported by the editor or
"""|When this option is enabled, each method that has implicit arguments has them
|displayed either as additional decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
Expand All @@ -252,8 +252,8 @@ object UserConfiguration {
"false",
"false",
"Should display implicit conversion at usage sites",
"""|When this option is enabled, each place where an implicit method or class is used has it
|displayed either as additional decorations if they are supported by the editor or
"""|When this option is enabled, each place where an implicit method or class is used has it
|displayed either as additional decorations if they are supported by the editor or
|shown in the hover.
|""".stripMargin,
),
Expand All @@ -280,7 +280,7 @@ object UserConfiguration {
BuildInfo.scala3,
BuildInfo.scala3,
"Default fallback Scala version",
"""|The Scala compiler version that is used as the default or fallback in case a file
"""|The Scala compiler version that is used as the default or fallback in case a file
|doesn't belong to any build target or the specified Scala version isn't supported by Metals.
|This applies to standalone Scala files, worksheets, and Ammonite scripts.
""".stripMargin,
Expand Down Expand Up @@ -343,14 +343,14 @@ object UserConfiguration {
),
UserConfigurationOption(
"telemetry-level",
TelemetryLevel.default.stringValue,
TelemetryLevel.All.stringValue,
TelemetryLevel.default.textValue,
TelemetryLevel.Anonymous.textValue,
"Scope of reported telemetry data",
s"""Control what kind of telemetry events can be send to maintainers of Metals.
|With `${TelemetryLevel.Off.stringValue}` no telemetry data would be send.
|Minimal recommended level is `${TelemetryLevel.Error.stringValue}` which would collect diagnostic information when Metals components would crash or fail unexpectedly, allowing to understand why the problem occoured.
|The highest telemtetry level `${TelemetryLevel.All.stringValue}` allows to collect all information including how features are used to help us priortize future improvements."
|Defaults to `${TelemetryLevel.default.stringValue}`.
|With `${TelemetryLevel.Off.textValue}` no telemetry data would be send.
|Minimal recommended level is `${TelemetryLevel.Anonymous.textValue}` which would collect diagnostic information when Metals components would crash or fail unexpectedly, allowing to understand why the problem occoured.
|The highest telemtetry level `${TelemetryLevel.Full.textValue}` allows to collect all information including how features are used to help us priortize future improvements."
|Defaults to `${TelemetryLevel.default.textValue}`.
|""".stripMargin,
),
)
Expand Down Expand Up @@ -568,8 +568,8 @@ object UserConfiguration {
getBooleanKey("verbose-compilation").getOrElse(false)

val telemetryLevel = getStringKey("telemetry-level")
.flatMap(TelemetryLevel.fromString)
.getOrElse(TelemetryLevel.discover)
.map(TelemetryLevel.fromString)
.getOrElse(TelemetryLevel.default)

if (errors.isEmpty) {
Right(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,45 @@
package scala.meta.internal.telemetry

import java.io.InputStreamReader

import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.Random
import scala.util.Try
import scala.util.control.NonFatal
import scala.util.Success

import scala.meta.internal.metals.LoggerAccess
import scala.meta.internal.metals.TelemetryLevel
import scala.meta.internal.telemetry

import com.google.gson.JsonSyntaxException
import requests.Response

object TelemetryClient {
private class DeserializationException(message: String)
extends RuntimeException(message)

case class Config(serverHost: String)
object Config {
// private final val DefaultTelemetryEndpoint =
// "https://scala3.westeurope.cloudapp.azure.com/telemetry"
private final val DefaultTelemetryEndpoint =
"http://localhost:8081"

private final val DefaultTelemetryEndpoint = "http://localhost:8081"
val default: Config = Config(DefaultTelemetryEndpoint)
}

private class Endpoint[-In, +Out](
endpoint: telemetry.ServiceEndpoint[In, Out]
)(implicit config: Config) {
def apply(data: In): Try[Out] = {
val json = encodeRequest(data)
execute(json).map(decodeResponse)
private class TelemetryRequest[In](
endpoint: telemetry.FireAndForgetEndpoint[In],
logger: LoggerAccess,
)(implicit config: Config, ec: ExecutionContext) {
private val endpointURL = s"${config.serverHost}${endpoint.uri}"
private val requester = requests.send(endpoint.method)
println(s"TelemetryClient: sending $endpointURL")

def apply(data: In): Unit = {
val json = endpoint.encodeInput(data)
val response = execute(json)
acknowledgeResponse(response)
}

private val endpointURL = s"${config.serverHost}${endpoint.getUri()}"
private val requester = requests.send(endpoint.getMethod())

private def execute(
data: String,
retries: Int = 3,
backoffMillis: Int = 100,
): Try[Response] = Try {
): Future[Response] = Future {
requester(
url = endpointURL,
data = data,
Expand All @@ -56,62 +53,37 @@ object TelemetryClient {
execute(data, retries - 1, backoffMillis + Random.nextInt(1000))
}

private def encodeRequest(request: In): String =
telemetry.GsonCodecs.gson.toJson(request)

private def decodeResponse(response: Response): Out = {
if (response.is2xx) {
val outputType = endpoint.getOutputType()
if (outputType == classOf[Void]) ().asInstanceOf[Out]
else {
response.readBytesThrough { is =>
try
telemetry.GsonCodecs.gson
.fromJson(new InputStreamReader(is), outputType)
catch {
case err: JsonSyntaxException =>
throw new DeserializationException(err.getMessage())
}
}
}
} else
throw new IllegalStateException(
s"${endpoint.getMethod()}:${endpoint.getUri()} should never result in error, got ${response}"
)
}
private def acknowledgeResponse(response: Future[Response]): Unit =
response.onComplete {
case Success(value) if value.is2xx =>
case _ =>
logger.debug(
s"${endpoint.method}:${endpoint.uri} should never result in error, got ${response}"
)
}
}
}

private[meta] class TelemetryClient(
telemetryLevel: () => TelemetryLevel,
config: TelemetryClient.Config = TelemetryClient.Config.default,
logger: LoggerAccess = LoggerAccess.system,
) extends telemetry.TelemetryService {
import telemetry.{TelemetryService => api}
)(implicit ec: ExecutionContext)
extends telemetry.TelemetryService {
import TelemetryClient._
import telemetry.TelemetryService._

implicit private def clientConfig: Config = config

private val SendErrorReport = new Endpoint(api.SendErrorReportEndpoint)
private val SendCrashReport = new Endpoint(api.SendCrashReportEndpoint)

override def sendErrorReport(report: telemetry.ErrorReport): Unit =
if (telemetryLevel().reportErrors) {
SendErrorReport(report)
.recover { case NonFatal(err) =>
logSendFailure(reportType = "error")(err)
}
}
private val sendErrorReport0 =
new TelemetryRequest(sendErrorReportEndpoint, logger)
private val sendCrashReport0 =
new TelemetryRequest(sendCrashReportEndpoint, logger)

override def sendCrashReport(report: telemetry.CrashReport): Unit =
if (telemetryLevel().reportCrash) {
SendCrashReport(report)
.recover { case NonFatal(err) =>
logSendFailure(reportType = "crash")(err)
}
}
def sendErrorReport(report: telemetry.ErrorReport): Unit =
if (telemetryLevel().enabled) sendErrorReport0(report)

private def logSendFailure(reportType: String)(error: Throwable) =
logger.debug(s"Failed to send $reportType report: ${error}")
def sendCrashReport(report: telemetry.CrashReport): Unit =
if (telemetryLevel().enabled) sendCrashReport0(report)

}
Loading

0 comments on commit 69c708f

Please sign in to comment.