Skip to content

Sub events #7

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

Merged
merged 4 commits into from
Dec 8, 2021
Merged
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
9 changes: 6 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ version = cliVersion

repositories {
mavenCentral()
jcenter()
maven(url = "https://jitpack.io") { name = "jitpack" }
}

Expand All @@ -37,7 +36,11 @@ dependencies {

implementation("com.github.sourceplusplus.protocol:protocol:$protocolVersion")

implementation("org.slf4j:slf4j-api:1.7.32")
implementation("org.slf4j:slf4j-nop:1.7.32")
implementation("io.vertx:vertx-core:$vertxVersion")
implementation("io.vertx:vertx-tcp-eventbus-bridge:$vertxVersion")
implementation("io.vertx:vertx-lang-kotlin-coroutines:$vertxVersion")
implementation("org.apache.commons:commons-lang3:$commonsLang3Version")
implementation("com.github.ajalt.clikt:clikt:$cliktVersion")
implementation("org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion")
Expand All @@ -49,6 +52,7 @@ dependencies {
implementation("eu.geekplace.javapinning:java-pinning-core:1.2.0")

implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1")

testImplementation("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
}
Expand All @@ -75,7 +79,7 @@ tasks.create("createProperties") {
tasks["processResources"].dependsOn("createProperties")

graal {
//graalVersion(graalVersion.toString())
graalVersion(project.properties["graalVersion"] as String)
mainClass("spp.cli.Main")
outputName("spp-cli")
option("-H:+PrintClassInitialization")
Expand All @@ -100,7 +104,6 @@ tasks.getByName("build").dependsOn("shadowJar")

configurations.runtimeClasspath {
exclude("ch.qos.logback", "logback-classic")
exclude("org.slf4j", "slf4j-api")
}

tasks.getByName<Test>("test") {
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ kotlin.code.style=official
cliGroup = com.sourceplusplus
cliVersion = 0.2.2

protocolVersion=0.2.2
protocolVersion=0.2.3

#SkyWalking 8.9.0-compatible
vertxVersion = 4.0.2

graalVersion = 20.2.0
graalVersion = 21.3.0
jacksonVersion = 2.12.5
apolloVersion = 2.5.11
commonsLang3Version = 3.12.0
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/spp/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ object Main {
RemoveInstrument(),
RemoveInstruments(),
ClearInstruments(),
SubscribeEvents(),
//etc
Version()
).main(args)
Expand Down
42 changes: 27 additions & 15 deletions src/main/kotlin/spp/cli/PlatformCLI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ package spp.cli
import com.apollographql.apollo.ApolloClient
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.fasterxml.jackson.databind.module.SimpleModule
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.file
import eu.geekplace.javapinning.JavaPinning
import io.vertx.core.json.jackson.DatabindCodec
import kotlinx.datetime.Clock
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.Instant
import kotlinx.datetime.plus
import okhttp3.OkHttpClient
import okhttp3.Request
import org.bouncycastle.cert.X509CertificateHolder
Expand All @@ -18,22 +24,21 @@ import org.bouncycastle.openssl.PEMKeyPair
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import org.bouncycastle.util.encoders.Hex
import spp.protocol.util.KSerializers
import java.io.File
import java.io.StringReader
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.security.interfaces.RSAPrivateKey
import java.security.interfaces.RSAPublicKey
import java.time.Instant
import java.time.temporal.ChronoUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

object PlatformCLI : CliktCommand(name = "spp-cli", allowMultipleSubcommands = true) {

val verbose by option("-v", "--verbose", help = "Enable verbose mode").flag()
private val platformHost: String by option("-p", "--platform", help = "Source++ platform host")
val platformHost: String by option("-p", "--platform", help = "Source++ platform host")
.default(
(if (System.getenv("SPP_DISABLE_TLS") != "true") "https://" else "http://")
+ (System.getenv("SPP_PLATFORM_HOST") ?: "localhost") + ":5445"
Expand All @@ -44,8 +49,24 @@ object PlatformCLI : CliktCommand(name = "spp-cli", allowMultipleSubcommands = t
.default(File("config/spp-platform.key"))
private val accessToken by option("-a", "--access-token", help = "Developer access token")
val apolloClient: ApolloClient by lazy { connectToPlatform() }
val certFingerprint: String? by lazy {
if (platformCertificate.exists()) {
val crtFileData = platformCertificate.readText()
val crtParser = PEMParser(StringReader(crtFileData))
val crtHolder = crtParser.readObject() as X509CertificateHolder
val certificate = JcaX509CertificateConverter().getCertificate(crtHolder)!!
fingerprint(certificate)
} else {
null
}
}

override fun run() = Unit
override fun run() {
val module = SimpleModule()
module.addSerializer(Instant::class.java, KSerializers.KotlinInstantSerializer())
module.addDeserializer(Instant::class.java, KSerializers.KotlinInstantDeserializer())
DatabindCodec.mapper().registerModule(module)
}

private fun connectToPlatform(): ApolloClient {
val serverUrl = if (platformHost.startsWith("http")) {
Expand All @@ -54,15 +75,6 @@ object PlatformCLI : CliktCommand(name = "spp-cli", allowMultipleSubcommands = t
"https://$platformHost"
}

var certFingerprint: String? = null
if (platformCertificate.exists()) {
val crtFileData = platformCertificate.readText()
val crtParser = PEMParser(StringReader(crtFileData))
val crtHolder = crtParser.readObject() as X509CertificateHolder
val certificate = JcaX509CertificateConverter().getCertificate(crtHolder)!!
certFingerprint = fingerprint(certificate)
}

val httpClient = if (certFingerprint != null) {
OkHttpClient().newBuilder()
.hostnameVerifier { _, _ -> true }
Expand Down Expand Up @@ -97,8 +109,8 @@ object PlatformCLI : CliktCommand(name = "spp-cli", allowMultipleSubcommands = t
jwtToken = JWT.create()
.withIssuer("cli")
.withClaim("developer_id", "system") //users with key are automatically considered system
.withClaim("created_at", Instant.now().toEpochMilli())
.withClaim("expires_at", Instant.now().plus(365, ChronoUnit.DAYS).toEpochMilli())
.withClaim("created_at", Clock.System.now().toEpochMilliseconds())
.withClaim("expires_at", Clock.System.now().plus(8760, DateTimeUnit.HOUR).toEpochMilliseconds())
.sign(algorithm)
} else {
val tokenUri = "$serverUrl/api/new-token?access_token=$accessToken"
Expand Down
157 changes: 157 additions & 0 deletions src/main/kotlin/spp/cli/commands/instrument/SubscribeEvents.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package spp.cli.commands.instrument

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import eu.geekplace.javapinning.JavaPinning
import eu.geekplace.javapinning.pin.Pin
import io.vertx.core.Vertx
import io.vertx.core.json.Json
import io.vertx.core.json.JsonObject
import io.vertx.core.net.NetClientOptions
import io.vertx.core.net.TrustOptions
import io.vertx.ext.bridge.BridgeEventType
import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper
import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameParser
import io.vertx.kotlin.coroutines.await
import kotlinx.coroutines.runBlocking
import spp.cli.PlatformCLI
import spp.protocol.SourceMarkerServices
import spp.protocol.extend.TCPServiceFrameParser
import spp.protocol.instrument.LiveInstrumentEvent
import spp.protocol.instrument.LiveInstrumentEventType
import spp.protocol.instrument.breakpoint.event.LiveBreakpointHit
import spp.protocol.instrument.breakpoint.event.LiveBreakpointRemoved
import spp.protocol.instrument.log.event.LiveLogHit
import spp.protocol.instrument.log.event.LiveLogRemoved

class SubscribeEvents : CliktCommand(
help = "Listens for and outputs live events. Subscribes to all events by default"
) {

val instrumentIds by argument(
name = "Instrument IDs",
help = "Capture events from specific live instruments"
).multiple()

val includeBreakpoints by option("--breakpoints", "-b", help = "Include live breakpoint events")
.flag(default = false)
val includeLogs by option("--logs", "-l", help = "Include live log events")
.flag(default = false)
val includeMeters by option("--meters", "-m", help = "Include live meter events")
.flag(default = false)
val includeTraces by option("--traces", "-t", help = "Include live trace events")
.flag(default = false)

override fun run() {
var eventCount = 1
runBlocking {
val vertx = Vertx.vertx()
val client = if (PlatformCLI.certFingerprint != null) {
val options = NetClientOptions()
.setReconnectAttempts(Int.MAX_VALUE).setReconnectInterval(5000)
.setSsl(PlatformCLI.platformHost.startsWith("https"))
.setTrustOptions(
TrustOptions.wrap(
JavaPinning.trustManagerForPins(listOf(Pin.fromString("CERTSHA256:${PlatformCLI.certFingerprint}")))
)
)
vertx.createNetClient(options)
} else {
val options = NetClientOptions()
.setReconnectAttempts(Int.MAX_VALUE).setReconnectInterval(5000)
.setSsl(PlatformCLI.platformHost.startsWith("https"))
vertx.createNetClient(options)
}
val socket = client.connect(
5455,
PlatformCLI.platformHost.substringAfter("https://").substringAfter("http://")
.substringBefore(":")
).await()
socket!!.handler(FrameParser(TCPServiceFrameParser(vertx, socket)))

vertx.eventBus().consumer<JsonObject>("local." + SourceMarkerServices.Provide.LIVE_INSTRUMENT_SUBSCRIBER) {
val liveEvent = Json.decodeValue(it.body().toString(), LiveInstrumentEvent::class.java)

//todo: impl filter on platform
if (instrumentIds.isNotEmpty()) {
when (liveEvent.eventType) {
LiveInstrumentEventType.LOG_HIT -> {
val logHit = Json.decodeValue(liveEvent.data, LiveLogHit::class.java)
if (logHit.logId !in instrumentIds) {
return@consumer
}
}
LiveInstrumentEventType.BREAKPOINT_HIT -> {
val breakpointHit = Json.decodeValue(liveEvent.data, LiveBreakpointHit::class.java)
if (breakpointHit.breakpointId !in instrumentIds) {
return@consumer
}
}
LiveInstrumentEventType.BREAKPOINT_REMOVED -> {
val breakpointRemoved = Json.decodeValue(liveEvent.data, LiveBreakpointRemoved::class.java)
if (breakpointRemoved.breakpointId !in instrumentIds) {
return@consumer
}
}
LiveInstrumentEventType.LOG_REMOVED -> {
val logRemoved = Json.decodeValue(liveEvent.data, LiveLogRemoved::class.java)
if (logRemoved.logId !in instrumentIds) {
return@consumer
}
}
else -> TODO("Unhandled event type: ${liveEvent.eventType}")
}
}

if (!includeBreakpoints && !includeLogs && !includeMeters && !includeTraces) {
//listen for all events
println(
"\nEvent (${eventCount++}):\n" +
"\tType: ${liveEvent.eventType}\n" +
"\tData: ${liveEvent.data}"
)
} else {
//todo: impl filtering on platform
//listen for specific events
if (includeBreakpoints && liveEvent.eventType.name.startsWith("breakpoint", true)) {
println(
"\nEvent (${eventCount++}):\n" +
"\tType: ${liveEvent.eventType}\n" +
"\tData: ${liveEvent.data}"
)
} else if (includeLogs && liveEvent.eventType.name.startsWith("log", true)) {
println(
"\nEvent (${eventCount++}):\n" +
"\tType: ${liveEvent.eventType}\n" +
"\tData: ${liveEvent.data}"
)
} else if (includeMeters && liveEvent.eventType.name.startsWith("meter", true)) {
println(
"\nEvent (${eventCount++}):\n" +
"\tType: ${liveEvent.eventType}\n" +
"\tData: ${liveEvent.data}"
)
} else if (includeTraces && liveEvent.eventType.name.startsWith("trace", true)) {
println(
"\nEvent (${eventCount++}):\n" +
"\tType: ${liveEvent.eventType}\n" +
"\tData: ${liveEvent.data}"
)
}
}
}

//register listener
FrameHelper.sendFrame(
BridgeEventType.REGISTER.name.toLowerCase(),
SourceMarkerServices.Provide.LIVE_INSTRUMENT_SUBSCRIBER,
JsonObject(),
socket
)
println("Listening for events...")
}
}
}
34 changes: 33 additions & 1 deletion src/main/resources/META-INF/native-image/spp/cli/jni-config.json
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
[]
[
{
"name":"java.lang.Boolean",
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]}
,
{
"name":"java.lang.ClassLoader",
"methods":[
{"name":"getPlatformClassLoader","parameterTypes":[] },
{"name":"loadClass","parameterTypes":["java.lang.String"] }
]}
,
{
"name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader"}
,
{
"name":"org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints",
"methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }]}
,
{
"name":"sun.management.VMManagementImpl",
"fields":[
{"name":"compTimeMonitoringSupport"},
{"name":"currentThreadCpuTimeSupport"},
{"name":"objectMonitorUsageSupport"},
{"name":"otherThreadCpuTimeSupport"},
{"name":"remoteDiagnosticCommandsSupport"},
{"name":"synchronizerUsageSupport"},
{"name":"threadAllocatedMemorySupport"},
{"name":"threadContentionMonitoringSupport"}
]}

]
Loading