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

Bisq Network Monitor: Babysteps #2181

Merged
merged 45 commits into from
Dec 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
df5b2dc
Basic framework for Monitor
freimair Dec 10, 2018
86d6425
Basic configuration infrastructure available
freimair Dec 10, 2018
984aabf
Periodical schedule
freimair Dec 10, 2018
52c5a7e
Use configurable properties file
freimair Dec 10, 2018
4796f1b
Reloading config during runtime available
freimair Dec 10, 2018
da483a2
Metrics can be disabled via config
freimair Dec 10, 2018
4d39d6b
Basic configuration tests
freimair Dec 10, 2018
a73c24f
Metrics can be reenabled via config
freimair Dec 10, 2018
930cb8c
Metrics can be renamed
freimair Dec 11, 2018
bb26694
Use lombok/slf4j for logging
freimair Dec 11, 2018
6481543
Configure gradle properly
freimair Dec 11, 2018
414623b
Introduce general purpose Tor instance
freimair Dec 18, 2018
0f8c420
Synchronize all the things
freimair Dec 18, 2018
9394da1
Introduce reporting stub
freimair Dec 18, 2018
677ed23
Add initial TorStartupTime metric
freimair Dec 18, 2018
1d6e101
Enhance config file documentation
freimair Dec 18, 2018
6b43b47
Cleanup
freimair Dec 18, 2018
4d0a094
Configurable socksPort for TorStartupTime Metric
freimair Dec 18, 2018
fb9c11f
TorRoundTripTime Metric available
freimair Dec 18, 2018
612c5f3
Refactored Metrics scheduler
freimair Dec 19, 2018
bff347b
TorRoundtripTime Metric does samples
freimair Dec 19, 2018
295cb6c
Graceful shutdown, Netlayer 0.6.2
freimair Dec 21, 2018
d46813c
TorHiddenServiceStartupTime Metric available
freimair Dec 18, 2018
2db74a1
Refactored TorStartupTime
freimair Dec 21, 2018
973bcf5
Introduce reporter concept
freimair Dec 28, 2018
09e02ba
Refactored Metric
freimair Dec 28, 2018
a7d86f3
Refactored metric infrastructure
freimair Dec 28, 2018
26f6088
Graphite reporter is available
freimair Dec 28, 2018
ada2e58
Pretty up config file
freimair Dec 28, 2018
7cf083b
Readme, Javadoc, cleanup
freimair Dec 29, 2018
59e5285
Dependencies and Buildfile cleanup
freimair Dec 29, 2018
f329370
Fixed compiation error as in: Commit of shame
freimair Dec 29, 2018
d4db4d8
Reformat code, organize imports, fix spelling
ManfredKarrer Dec 29, 2018
2762d74
Rename TorRoundtripTime to TorRoundTripTime
ManfredKarrer Dec 29, 2018
2d8ca8c
Add monitor directory as parent for test directories
ManfredKarrer Dec 29, 2018
cb533f4
Update gitignore
ManfredKarrer Dec 29, 2018
3661218
Removed unused dummy Metric
freimair Dec 29, 2018
1c58d89
Apply code inspection
ManfredKarrer Dec 29, 2018
69b168e
Add guava
ManfredKarrer Dec 29, 2018
579d1f1
Use 1.1.10 instead of $logbackVersion
ManfredKarrer Dec 29, 2018
e39f91a
Update hashes
ManfredKarrer Dec 29, 2018
b18c0b0
Ignore tests
ManfredKarrer Dec 29, 2018
cfec714
Merge branch 'master' into monitor
freimair Dec 30, 2018
decd648
Merge branch 'monitor-nits' of git://github.com/ManfredKarrer/bisq in…
freimair Dec 30, 2018
32650f1
Merge branch 'ManfredKarrer-monitor-nits' into monitor
freimair Dec 30, 2018
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ desktop.ini
*.class
deploy
*/releases/*
/monitor/TorHiddenServiceStartupTimeTests/*
/monitor/monitor-tor/*
35 changes: 28 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ configure(subprojects) {
joptVersion = '5.0.3'
langVersion = '3.4'
libdohjVersion = '7be803fa'
logbackVersion = '1.1.10'
lombokVersion = '1.18.2'
mockitoVersion = '2.21.0'
powermockVersion = '2.0.0-beta.5'
protobufVersion = '3.5.1'
slf4jVersion = '1.7.22'
sparkVersion = '2.5.2'
springVersion = '4.3.6.RELEASE'

Expand Down Expand Up @@ -159,9 +161,9 @@ configure(project(':common')) {
exclude(module: 'junit')
}
compile "org.springframework:spring-core:$springVersion"
compile 'org.slf4j:slf4j-api:1.7.22'
compile 'ch.qos.logback:logback-core:1.1.10'
compile 'ch.qos.logback:logback-classic:1.1.10'
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-core:$logbackVersion"
compile "ch.qos.logback:logback-classic:$logbackVersion"
compile 'com.google.code.findbugs:jsr305:3.0.2'
compile 'com.google.guava:guava:20.0'
compile('com.google.inject:guice:4.1.0') {
Expand Down Expand Up @@ -310,14 +312,33 @@ configure(project(':desktop')) {


configure(project(':monitor')) {
mainClassName = 'bisq.monitor.MonitorMain'
mainClassName = 'bisq.monitor.Monitor'

test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}

dependencies {
compile project(':core')
compile "com.sparkjava:spark-core:$sparkVersion"
compile 'net.gpedro.integrations.slack:slack-webhook:1.1.1'
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-core:$logbackVersion"
compile "ch.qos.logback:logback-classic:$logbackVersion"
compile 'com.google.guava:guava:20.0'

compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"

compile('com.github.JesusMcCloud.netlayer:tor.native:0.6.2') {
exclude(module: 'slf4j-api')
}

testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.2'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.3.2'
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
}
}

Expand Down
1 change: 1 addition & 0 deletions gradle/witness/gradle-witness.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// 4. Commit the changes
//
// See https://github.com/signalapp/gradle-witness#using-witness for further details.

dependencyVerification {
verify = [
'org.controlsfx:controlsfx:b98f1c9507c05600f80323674b33d15674926c71b0116f70085b62bdacf1e573',
Expand Down
57 changes: 57 additions & 0 deletions monitor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Bisq Network Monitor Node

The Bisq monitor node collects a set of metrics which are of interest to developers and users alike. These metrics are then made available through reporters.

The *Babysteps* release features these metrics:
- Tor Startup Time: The time it takes to start Tor starting at a clean system, unpacking the shipped Tor binaries, firing up Tor until Tor is connected to the Tor network and ready to use.
- Tor Roundtrip Time: Given a bootstrapped Tor, the roundtrip time of connecting to a hidden service is measured.
- Tor Hidden Service Startup Time: Given a bootstrapped Tor, the time it takes to create and announce a freshly created hidden service.

The *Babysteps* release features these reporters:
- A reporter that simply writes the findings to `System.err`
- A reporter that reports the findings to a Graphite/Carbon instance using the [plaintext protocol](https://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol)

## Configuration

The *Bisq Network Monitor Node* is to be configured via a Java properties file. The location of the file is to be passed as command line parameter:

```
./bisq-monitor /path/to/your/config.properties
```

A sample configuration file looks like follows:

```
## Each Metric is configured via a set of properties.
##
## The minimal set of properties required to run a Metric is:
##
## YourMetricName.enabled=true|false
## YourMetricName.run.interval=10 [seconds]

#Edit and uncomment the lines below for your liking

#TorStartupTime Metric
TorStartupTime.enabled=true
TorStartupTime.run.interval=100
TorStartupTime.run.socksPort=90500 # so that there is no interference with a system Tor

#TorRoundTripTime Metric
TorRoundTripTime.enabled=true
TorRoundTripTime.run.interval=100
TorRoundTripTime.run.sampleSize=5
TorRoundTripTime.run.hosts=http://expyuzz4wqqyqhjn.onion:80 # torproject.org hidden service

#TorHiddenServiceStartupTime Metric
TorHiddenServiceStartupTime.enabled=true
TorHiddenServiceStartupTime.run.interval=100
TorHiddenServiceStartupTime.run.localPort=90501 # so that there is no interference with a system Tor
TorHiddenServiceStartupTime.run.servicePort=90511 # so that there is no interference with a system Tor

## Reporters are configured via a set of properties as well.
##
## In contrast to Metrics, Reporters do not have a minimal set of properties.

#GraphiteReporter
GraphiteReporter.serviceUrl=http://yourHiddenService.onion:2003
```
74 changes: 74 additions & 0 deletions monitor/src/main/java/bisq/monitor/Configurable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.monitor;

import java.util.Properties;

/**
* Does some pre-computation for a configurable class.
*
* @author Florian Reimair
*/
freimair marked this conversation as resolved.
Show resolved Hide resolved
public abstract class Configurable {

protected Properties configuration = new Properties();

freimair marked this conversation as resolved.
Show resolved Hide resolved
private String name;

/**
* Filters all java properties starting with {@link Configurable#getName()} of
* the class and makes them available. Does <em>NOT</em> parse the content of
* the properties!
* <p>
* For example, if the implementing class sets its name (using
* {@link Configurable#setName(String)}) to <code>MyName</code>, the list of
* properties is scanned for properties starting with <code>MyName</code>.
* Matching lines are made available to the class without the prefix. For
* example, a property <code>MyName.answer=42</code> is made available as
* <code>configuration.getProperty("answer")</code> resulting in
* <code>42</code>.
*
* @param properties a set of configuration properties
*/
public void configure(final Properties properties) {
// only configure the Properties which belong to us
final Properties myProperties = new Properties();
properties.forEach((k, v) -> {
String key = (String) k;
if (key.startsWith(getName()))
myProperties.put(key.substring(key.indexOf(".") + 1), v);
});

// configure all properties that belong to us
this.configuration = myProperties;
}

protected String getName() {
return name;
}

/**
* Set the name used to filter through configuration properties. See
* {@link Configurable#configure(Properties)}.
*
* @param name
*/
protected void setName(String name) {
this.name = name;
}
}
155 changes: 155 additions & 0 deletions monitor/src/main/java/bisq/monitor/Metric.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.monitor;

import java.util.Properties;

import lombok.extern.slf4j.Slf4j;

/**
* Starts a Metric (in its own {@link Thread}), manages its properties and shuts
* it down gracefully. Furthermore, configuration updates and execution are done
* in a thread-save manner. Implementing classes only have to implement the
* {@link Metric#execute()} method.
*
* @author Florian Reimair
*/
@Slf4j
public abstract class Metric extends Configurable implements Runnable {

private static final String INTERVAL = "run.interval";
private volatile boolean shutdown = false;

/**
* our reporter
*/
protected final Reporter reporter;
private Thread thread = new Thread();

/**
* disable execution
*/
private void disable() {
shutdown = true;
}

/**
* enable execution
*/
private void enable() {
shutdown = false;

thread = new Thread(this);

// set human readable name
thread.setName(getName());

// set as daemon, so that the jvm does not terminate the thread
thread.setDaemon(true);

thread.start();
}

/**
* Constructor.
*/
protected Metric(Reporter reporter) {

this.reporter = reporter;

setName(this.getClass().getSimpleName());

// disable by default
disable();
}

protected boolean enabled() {
return !shutdown;
}

@Override
public void configure(final Properties properties) {
synchronized (this) {
log.info("{} (re)loading config...", getName());
super.configure(properties);
reporter.configure(properties);

// decide whether to enable or disable the task
if (configuration.isEmpty() || !configuration.getProperty("enabled", "false").equals("true")
|| !configuration.containsKey(INTERVAL)) {
disable();

// some informative log output
if (configuration.isEmpty())
log.error("{} is not configured at all. Will not run.", getName());
else if (!configuration.getProperty("enabled", "false").equals("true"))
log.info("{} is deactivated. Will not run.", getName());
else if (!configuration.containsKey(INTERVAL))
log.error("{} is missing mandatory '" + INTERVAL + "' property. Will not run.", getName());
else
log.error("{} is mis-configured. Will not run.", getName());
} else if (!enabled() && configuration.getProperty("enabled", "false").equals("true")) {
// check if this Metric got activated after being disabled.
// if so, resume execution
enable();
log.info("{} got activated. Starting up.", getName());
}
}
}

@Override
public void run() {
while (!shutdown) {
// if not, execute all the things
synchronized (this) {
execute();
}

// make sure our configuration is not changed in the moment we want to query it
String interval;
synchronized (this) {
interval = configuration.getProperty(INTERVAL);
}

// and go to sleep for the configured amount of time.
try {
Thread.sleep(Long.parseLong(interval) * 1000);
} catch (InterruptedException ignore) {
}
}
log.info("{} shutdown", getName());
}

/**
* Gets scheduled repeatedly.
*/
protected abstract void execute();

/**
* Initiate graceful shutdown of the Metric.
*/
public void shutdown() {
log.debug("{} shutdown requested", getName());
shutdown = true;
}

protected void join() throws InterruptedException {
thread.join();
}

}
Loading