Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tcpDebug option and tracing #618

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ plugins {
id("kotlin-language-server.kotlin-conventions")
}

val serverDebugPort = 4000
val debugPort = 8000
val debugArgs = "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n,quiet=y"
val debugArgs = "-agentlib:jdwp=transport=dt_socket,server=y,address=$debugPort,suspend=n,quiet=y"

val serverMainClassName = "org.javacs.kt.MainKt"
val applicationName = "kotlin-language-server"
Expand Down Expand Up @@ -88,6 +89,8 @@ tasks.register<JavaExec>("debugRun") {
standardInput = System.`in`

jvmArgs(debugArgs)
args(listOf("--tcpServerPort", serverDebugPort, "--level", "trace", "--tcpDebug"))

doLast { println("Using debug port $debugPort") }
}

Expand Down
7 changes: 5 additions & 2 deletions server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletableFuture.completedFuture

class KotlinLanguageServer(
val config: Configuration = Configuration()
val config: Configuration = Configuration(),
private val tcpDebug: Boolean = false
) : LanguageServer, LanguageClientAware, Closeable {
val databaseService = DatabaseService()
val classPath = CompilerClassPath(config.compiler, config.scripts, config.codegen, databaseService)
Expand Down Expand Up @@ -56,7 +57,9 @@ class KotlinLanguageServer(

override fun connect(client: LanguageClient) {
this.client = client
connectLoggingBackend()
if (!tcpDebug) {
connectLoggingBackend()
}

workspaces.connect(client)
textDocuments.connect(client)
Expand Down
13 changes: 10 additions & 3 deletions server/src/main/kotlin/org/javacs/kt/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@ import org.eclipse.lsp4j.launch.LSPLauncher
import org.javacs.kt.util.ExitingInputStream
import org.javacs.kt.util.tcpStartServer
import org.javacs.kt.util.tcpConnectToClient

class Args {
/*
* The language server can currently be launched in three modes:
* - Stdio, in which case no argument should be specified (used by default)
* - TCP Server, in which case the client has to connect to the specified tcpServerPort (used by the Docker image)
* - TCP Client, in which case the server will connect to the specified tcpClientPort/tcpClientHost (optionally used by VSCode)
*/

@Parameter(names = ["--tcpServerPort", "-sp"])
var tcpServerPort: Int? = null

@Parameter(names = ["--tcpClientPort", "-p"])
var tcpClientPort: Int? = null

@Parameter(names = ["--tcpClientHost", "-h"])
var tcpClientHost: String = "localhost"

@Parameter(names = ["--level"])
var logLevel: String = "info"

@Parameter(names = ["--tcpDebug"])
var tcpDebug: Boolean = false
}

fun main(argv: Array<String>) {
Expand All @@ -39,7 +45,8 @@ fun main(argv: Array<String>) {
tcpStartServer(it)
} ?: Pair(System.`in`, System.out)

val server = KotlinLanguageServer()
LOG.setLogLevel(args.logLevel)
val server = KotlinLanguageServer(tcpDebug = args.tcpDebug)
val threads = Executors.newSingleThreadExecutor { Thread(it, "client") }
val launcher = LSPLauncher.createServerLauncher(server, ExitingInputStream(inStream), outStream, threads) { it }

Expand Down
40 changes: 35 additions & 5 deletions shared/src/main/kotlin/org/javacs/kt/Logger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.javacs.kt.util.DelegatePrintStream

val LOG = Logger()

private class JULRedirector(private val downstream: Logger): Handler() {
private class JULRedirector(private val downstream: Logger) : Handler() {
override fun publish(record: LogRecord) {
when (record.level) {
Level.SEVERE -> downstream.error(record.message)
Expand Down Expand Up @@ -41,12 +41,23 @@ enum class LogLevel(val value: Int) {
ALL(-100)
}

fun String.toLogLevel(): LogLevel {
return when (this) {
"error" -> LogLevel.ERROR
"warn" -> LogLevel.WARN
"debug" -> LogLevel.DEBUG
"trace" -> LogLevel.TRACE
else -> LogLevel.INFO
}
}

class LogMessage(
val level: LogLevel,
val message: String
val message: String,
private val funName: String? = null,
) {
val formatted: String
get() = "[$level] $message"
get() = "[$level] ${funName?.let { "$it " } ?: ""}$message"
}

class Logger {
Expand All @@ -60,6 +71,7 @@ class Logger {
private val newline = System.lineSeparator()
val logTime = false
var level = LogLevel.INFO
var stackTracing = false;

fun logError(msg: LogMessage) {
if (errBackend == null) {
Expand All @@ -78,14 +90,24 @@ class Logger {
}

private fun logWithPlaceholdersAt(msgLevel: LogLevel, msg: String, placeholders: Array<out Any?>) {
val stackTraceElement = if (stackTracing) {
Throwable("Capturing stack trace for logging").stackTrace.firstOrNull { it.className != this::class.java.name }
} else {
null
}
if (level.value <= msgLevel.value) {
log(LogMessage(msgLevel, format(insertPlaceholders(msg, placeholders))))
log(LogMessage(msgLevel, format(insertPlaceholders(msg, placeholders)), stackTraceElement?.className))
}
}

inline fun logWithLambdaAt(msgLevel: LogLevel, msg: () -> String) {
val stackTraceElement = if (stackTracing) {
Throwable("Capturing stack trace for logging").stackTrace.firstOrNull { it.className != this::class.java.name }
} else {
null
}
if (level.value <= msgLevel.value) {
log(LogMessage(msgLevel, msg()))
log(LogMessage(msgLevel, msg(), stackTraceElement?.className))
}
}

Expand Down Expand Up @@ -119,6 +141,14 @@ class Logger {

inline fun deepTrace(msg: () -> String) = logWithLambdaAt(LogLevel.DEEP_TRACE, msg)

fun setLogLevel(level: String) {
val logLevel = level.toLogLevel()
if (logLevel.value <= LogLevel.TRACE.value) {
stackTracing = true
}
this.level = logLevel
}

fun connectJULFrontend() {
val rootLogger = java.util.logging.Logger.getLogger("")
rootLogger.addHandler(JULRedirector(this))
Expand Down