Add statistics module

This commit is contained in:
Manfred Karrer 2016-07-31 02:37:47 +02:00
parent 3cf11e69e6
commit ea75705085
7 changed files with 541 additions and 2 deletions

View file

@ -162,7 +162,7 @@ public class PeerManager implements ConnectionListener {
final boolean seedNode = isSeedNode(connection);
final Optional<NodeAddress> addressOptional = connection.getPeersNodeAddressOptional();
log.info("onConnection: peer = {}{}", (addressOptional.isPresent() ? addressOptional.get().hostName : "unknown address"), seedNode ? " (SeedNode)" : "");
log.info("onConnection: peer = {}{}", (addressOptional.isPresent() ? addressOptional.get().hostName : "not known yet"), seedNode ? " (SeedNode)" : "");
if (seedNode)
connection.setPeerType(Connection.PeerType.SEED_NODE);

1
pom.xml Executable file → Normal file
View file

@ -48,6 +48,7 @@
<module>headless</module>
<module>seednode</module>
<module>monitor</module>
<module>statistics</module>
<module>jdkfix</module>
</modules>

85
statistics/pom.xml Normal file
View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>io.bitsquare</groupId>
<version>0.4.9.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>statistics</artifactId>
<build>
<resources>
<resource>
<filtering>false</filtering>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.fxml</include>
<include>**/*.css</include>
</includes>
</resource>
<resource>
<filtering>false</filtering>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<!-- broken with Java 8 (MSHADE-174), using ProGuard instead. -->
<minimizeJar>false</minimizeJar>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>io.bitsquare.statistics.StatisticsMain</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<!-- exclude signatures, the bundling process breaks them for some reason -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>bundled</shadedClassifierName>
<finalName>Statistics</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.bitsquare</groupId>
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,160 @@
package io.bitsquare.statistics;
import ch.qos.logback.classic.Level;
import com.google.inject.Guice;
import com.google.inject.Injector;
import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.app.Log;
import io.bitsquare.app.Version;
import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.P2PServiceListener;
import io.bitsquare.trade.offer.OpenOfferManager;
import io.bitsquare.trade.statistics.TradeStatisticsManager;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.bitcoinj.store.BlockStoreException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.nio.file.Paths;
import java.security.Security;
public class Statistics {
private static final Logger log = LoggerFactory.getLogger(Statistics.class);
private static Environment env;
private final Injector injector;
private final StatisticsModule statisticsModule;
private final TradeStatisticsManager tradeStatisticsManager;
private P2PService p2pService;
public static void setEnvironment(Environment env) {
Statistics.env = env;
}
public Statistics() {
String logPath = Paths.get(env.getProperty(CoreOptionKeys.APP_DATA_DIR_KEY), "bitsquare").toString();
Log.setup(logPath);
log.info("Log files under: " + logPath);
Version.printVersion();
Utilities.printSysInfo();
Log.setLevel(Level.toLevel(env.getRequiredProperty(CommonOptionKeys.LOG_LEVEL_KEY)));
// setup UncaughtExceptionHandler
Thread.UncaughtExceptionHandler handler = (thread, throwable) -> {
// Might come from another thread
if (throwable.getCause() != null && throwable.getCause().getCause() != null &&
throwable.getCause().getCause() instanceof BlockStoreException) {
log.error(throwable.getMessage());
} else {
log.error("Uncaught Exception from thread " + Thread.currentThread().getName());
log.error("throwableMessage= " + throwable.getMessage());
log.error("throwableClass= " + throwable.getClass());
log.error("Stack trace:\n" + ExceptionUtils.getStackTrace(throwable));
throwable.printStackTrace();
}
};
Thread.setDefaultUncaughtExceptionHandler(handler);
Thread.currentThread().setUncaughtExceptionHandler(handler);
if (Utilities.isRestrictedCryptography())
Utilities.removeCryptographyRestrictions();
Security.addProvider(new BouncyCastleProvider());
statisticsModule = new StatisticsModule(env);
injector = Guice.createInjector(statisticsModule);
Version.setBtcNetworkId(injector.getInstance(BitsquareEnvironment.class).getBitcoinNetwork().ordinal());
p2pService = injector.getInstance(P2PService.class);
p2pService.start(false, new P2PServiceListener() {
@Override
public void onRequestingDataCompleted() {
}
@Override
public void onNoSeedNodeAvailable() {
}
@Override
public void onNoPeersAvailable() {
}
@Override
public void onBootstrapComplete() {
}
@Override
public void onTorNodeReady() {
}
@Override
public void onHiddenServicePublished() {
}
@Override
public void onSetupFailed(Throwable throwable) {
}
@Override
public void onUseDefaultBridges() {
}
@Override
public void onRequestCustomBridges(Runnable resultHandler) {
}
});
// We want to persist trade statistics so we need to instantiate the tradeStatisticsManager
tradeStatisticsManager = injector.getInstance(TradeStatisticsManager.class);
}
public void shutDown() {
gracefulShutDown(() -> {
log.debug("Shutdown complete");
System.exit(0);
});
}
public void gracefulShutDown(ResultHandler resultHandler) {
log.debug("gracefulShutDown");
try {
if (injector != null) {
injector.getInstance(ArbitratorManager.class).shutDown();
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
injector.getInstance(P2PService.class).shutDown(() -> {
injector.getInstance(WalletService.class).shutDownDone.addListener((ov, o, n) -> {
statisticsModule.close(injector);
log.debug("Graceful shutdown completed");
resultHandler.handleResult();
});
injector.getInstance(WalletService.class).shutDown();
});
});
// we wait max 5 sec.
UserThread.runAfter(resultHandler::handleResult, 5);
} else {
UserThread.runAfter(resultHandler::handleResult, 1);
}
} catch (Throwable t) {
log.debug("App shutdown failed with exception");
t.printStackTrace();
System.exit(1);
}
}
}

View file

@ -0,0 +1,132 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare 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.
*
* Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.statistics;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.app.BitsquareExecutable;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Profiler;
import io.bitsquare.common.util.RestartUtil;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import static io.bitsquare.app.BitsquareEnvironment.DEFAULT_APP_NAME;
import static io.bitsquare.app.BitsquareEnvironment.DEFAULT_USER_DATA_DIR;
public class StatisticsMain extends BitsquareExecutable {
private static final Logger log = LoggerFactory.getLogger(StatisticsMain.class);
private static final long MAX_MEMORY_MB = 600;
private static final long CHECK_MEMORY_PERIOD_SEC = 10 * 60;
private Statistics statistics;
private volatile boolean stopped;
public static void main(String[] args) throws Exception {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("Statistics")
.setDaemon(true)
.build();
UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
// We don't want to do the full argument parsing here as that might easily change in update versions
// So we only handle the absolute minimum which is APP_NAME, APP_DATA_DIR_KEY and USER_DATA_DIR
BitsquareEnvironment.setDefaultAppName("Bitsquare_statistics");
OptionParser parser = new OptionParser();
parser.allowsUnrecognizedOptions();
parser.accepts(CoreOptionKeys.USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
.withRequiredArg();
parser.accepts(CoreOptionKeys.APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
.withRequiredArg();
OptionSet options;
try {
options = parser.parse(args);
} catch (OptionException ex) {
System.out.println("error: " + ex.getMessage());
System.out.println();
parser.printHelpOn(System.out);
System.exit(EXIT_FAILURE);
return;
}
BitsquareEnvironment bitsquareEnvironment = new BitsquareEnvironment(options);
// need to call that before BitsquareAppMain().execute(args)
BitsquareExecutable.initAppDir(bitsquareEnvironment.getProperty(CoreOptionKeys.APP_DATA_DIR_KEY));
// For some reason the JavaFX launch process results in us losing the thread context class loader: reset it.
// In order to work around a bug in JavaFX 8u25 and below, you must include the following code as the first line of your realMain method:
Thread.currentThread().setContextClassLoader(StatisticsMain.class.getClassLoader());
new StatisticsMain().execute(args);
}
@Override
protected void doExecute(OptionSet options) {
final BitsquareEnvironment environment = new BitsquareEnvironment(options);
Statistics.setEnvironment(environment);
UserThread.execute(() -> statistics = new Statistics());
UserThread.runPeriodically(() -> {
Profiler.printSystemLoad(log);
long usedMemoryInMB = Profiler.getUsedMemoryInMB();
if (!stopped) {
if (usedMemoryInMB > (MAX_MEMORY_MB - 100)) {
log.warn("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
"We are over our memory warn limit and call the GC. usedMemoryInMB: {}" +
"\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n",
usedMemoryInMB);
System.gc();
usedMemoryInMB = Profiler.getUsedMemoryInMB();
Profiler.printSystemLoad(log);
}
if (usedMemoryInMB > MAX_MEMORY_MB) {
stopped = true;
statistics.gracefulShutDown(() -> {
try {
final String[] tokens = environment.getAppDataDir().split("_");
String logPath = "error_" + (tokens.length > 1 ? tokens[tokens.length - 2] : "") + ".log";
RestartUtil.restartApplication(logPath);
} catch (IOException e) {
log.error(e.toString());
e.printStackTrace();
} finally {
log.warn("Shutdown complete");
System.exit(0);
}
});
}
}
}, CHECK_MEMORY_PERIOD_SEC);
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ignore) {
}
}
}
}

View file

@ -0,0 +1,110 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare 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.
*
* Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.statistics;
import com.google.inject.Singleton;
import io.bitsquare.alert.AlertModule;
import io.bitsquare.app.AppModule;
import io.bitsquare.app.BitsquareEnvironment;
import io.bitsquare.arbitration.ArbitratorModule;
import io.bitsquare.btc.BitcoinModule;
import io.bitsquare.common.Clock;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.crypto.KeyStorage;
import io.bitsquare.crypto.EncryptionServiceModule;
import io.bitsquare.filter.FilterModule;
import io.bitsquare.p2p.P2PModule;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.TradeModule;
import io.bitsquare.trade.offer.OfferModule;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.io.File;
import static com.google.inject.name.Names.named;
class StatisticsModule extends AppModule {
private static final Logger log = LoggerFactory.getLogger(StatisticsModule.class);
public StatisticsModule(Environment env) {
super(env);
}
@Override
protected void configure() {
bind(KeyStorage.class).in(Singleton.class);
bind(KeyRing.class).in(Singleton.class);
bind(User.class).in(Singleton.class);
bind(Preferences.class).in(Singleton.class);
bind(Clock.class).in(Singleton.class);
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
File keyStorageDir = new File(env.getRequiredProperty(KeyStorage.DIR_KEY));
bind(File.class).annotatedWith(named(KeyStorage.DIR_KEY)).toInstance(keyStorageDir);
bind(BitsquareEnvironment.class).toInstance((BitsquareEnvironment) env);
// ordering is used for shut down sequence
install(tradeModule());
install(encryptionServiceModule());
install(arbitratorModule());
install(offerModule());
install(torModule());
install(bitcoinModule());
install(alertModule());
install(filterModule());
}
private TradeModule tradeModule() {
return new TradeModule(env);
}
private EncryptionServiceModule encryptionServiceModule() {
return new EncryptionServiceModule(env);
}
private ArbitratorModule arbitratorModule() {
return new ArbitratorModule(env);
}
private AlertModule alertModule() {
return new AlertModule(env);
}
private FilterModule filterModule() {
return new FilterModule(env);
}
private OfferModule offerModule() {
return new OfferModule(env);
}
private P2PModule torModule() {
return new P2PModule(env);
}
private BitcoinModule bitcoinModule() {
return new BitcoinModule(env);
}
}

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ This file is part of Bitsquare.
~
~ Bitsquare 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.
~
~ Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
-->
<configuration>
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n)</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="CONSOLE_APPENDER"/>
</root>
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
<logger name="io.bitsquare.storage.FileManager" level="WARN"/>
<!-- <logger name="io.bitsquare.p2p.peers" level="WARN"/>
<logger name="io.bitsquare.p2p.peers.getdata" level="WARN"/>
<logger name="io.bitsquare.p2p.peers.keepalive" level="WARN"/>
<logger name="io.bitsquare.p2p.peers.peerexchange" level="WARN"/>
<logger name="io.bitsquare.p2p.network" level="WARN"/>
<logger name="io.bitsquare.p2p.P2PService" level="WARN"/>-->
<!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="INFO"/>
<logger name="io.bitsquare.p2p.P2PService" level="INFO"/>
<logger name="io.bitsquare.p2p.storage.ProtectedExpirableDataStorage" level="INFO"/>
<logger name="io.bitsquare.p2p.network.LocalhostNetworkNode" level="INFO"/>
<logger name="io.bitsquare.p2p.network.TorNetworkNode" level="TRACE"/>
<logger name="io.bitsquare.p2p.network.NetworkNode" level="TRACE"/>-->
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="INFO"/>
</configuration>