Make Akka log to file correctly (#1801)

This commit is contained in:
Ben Carman 2020-08-12 07:52:51 -05:00 committed by GitHub
parent 5fc354fc7b
commit b3a8f1d76b
5 changed files with 287 additions and 162 deletions

View file

@ -10,4 +10,28 @@
<!-- see what's returned by Slick -->
<logger name="slick.jdbc.StatementInvoker.result" level="INFO"/>
<appender name="STDOUT" target="System.out" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ss,SSXXX} %level [%logger{0}] %msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>8192</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="STDOUT" />
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${bitcoins.log.location}</file>
<encoder>
<pattern>%date{yyyy-MM-dd'T'HH:mm:ss,SSXXX} %level [%logger{0}] %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ASYNC"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View file

@ -90,8 +90,27 @@ bitcoin-s {
akka {
loglevel = "INFO"
stdout-loglevel = "OFF"
# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs
# to STDOUT)
loggers = ["akka.event.slf4j.Slf4jLogger"]
# Log level used by the configured loggers (see "loggers") as soon
# as they have been started; before that, see "stdout-loglevel"
# Options: OFF, ERROR, WARNING, INFO, DEBUG
loglevel = "DEBUG"
# Log level for the very basic logger activated during ActorSystem startup.
# This logger prints the log messages to stdout (System.out).
# Options: OFF, ERROR, WARNING, INFO, DEBUG
stdout-loglevel = "DEBUG"
# Filter of log events that is used by the LoggingAdapter before
# publishing log events to the eventStream.
logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
use-slf4j = on
log-config-on-start = off
actor {
debug {

View file

@ -1,6 +1,6 @@
package org.bitcoins.server
import java.nio.file.Paths
import java.nio.file.{Path, Paths}
import akka.actor.ActorSystem
import akka.dispatch.Dispatchers
@ -14,6 +14,7 @@ import org.bitcoins.chain.models.{
CompactFilterHeaderDAO
}
import org.bitcoins.core.Core
import org.bitcoins.core.config.{BitcoinNetworks, MainNet, RegTest, TestNet3}
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil, NetworkUtil}
import org.bitcoins.db.AppConfig
import org.bitcoins.feeprovider.BitcoinerLiveFeeRateProvider
@ -28,21 +29,41 @@ import scala.concurrent.{ExecutionContext, Future, Promise}
object Main extends App with BitcoinSLogger {
private def runMain(): Unit = {
implicit val system: ActorSystem = ActorSystem("bitcoin-s")
implicit val ec: ExecutionContext = system.dispatcher
val argsWithIndex = args.zipWithIndex
implicit val conf: BitcoinSAppConfig = {
val dataDirIndexOpt = {
argsWithIndex.find(_._1.toLowerCase == "--datadir")
}
val datadirPath = dataDirIndexOpt match {
case None => AppConfig.DEFAULT_BITCOIN_S_DATADIR
case Some((_, dataDirIndex)) =>
val str = args(dataDirIndex + 1)
Paths.get(str)
}
val dataDirIndexOpt = {
argsWithIndex.find(_._1.toLowerCase == "--datadir")
}
val datadirPath = dataDirIndexOpt match {
case None => AppConfig.DEFAULT_BITCOIN_S_DATADIR
case Some((_, dataDirIndex)) =>
val str = args(dataDirIndex + 1)
Paths.get(str)
val baseConfig = AppConfig.getBaseConfig(datadirPath)
val networkStr = baseConfig.getString("bitcoin-s.network")
val network = BitcoinNetworks.fromString(networkStr)
val datadir: Path = {
val lastDirname = network match {
case MainNet => "mainnet"
case TestNet3 => "testnet3"
case RegTest => "regtest"
}
datadirPath.resolve(lastDirname)
}
System.setProperty("bitcoins.log.location",
datadir.resolve("bitcoin-s.log").toAbsolutePath.toString)
implicit val system: ActorSystem = ActorSystem("bitcoin-s", baseConfig)
implicit val ec: ExecutionContext = system.dispatcher
system.log.info("Akka logger started")
implicit val conf: BitcoinSAppConfig = {
BitcoinSAppConfig(datadirPath)
}

View file

@ -135,90 +135,17 @@ abstract class AppConfig extends LoggerConfig with StartStopAsync[Unit] {
* The underlying config that we derive the
* rest of the fields in this class from
*/
private[bitcoins] lazy val baseConfig: Config = {
AppConfig.getBaseConfig(baseDatadir, configOverrides)
}
private[bitcoins] lazy val config: Config = {
val datadirConfig = {
val file = baseDatadir.resolve("bitcoin-s.conf")
val config = if (Files.isReadable(file)) {
ConfigFactory.parseFile(file.toFile())
} else {
ConfigFactory.empty()
}
val withDatadir =
ConfigFactory.parseString(s"bitcoin-s.datadir = $baseDatadir")
withDatadir.withFallback(config)
}
logger.trace(s"Data directory config:")
if (datadirConfig.hasPath("bitcoin-s")) {
logger.trace(datadirConfig.getConfig("bitcoin-s").asReadableJson)
} else {
logger.trace(ConfigFactory.empty().asReadableJson)
}
// `load` tries to resolve substitions,
// `parseResources` does not
val dbConfig = ConfigFactory
.parseResources("db.conf")
logger.trace(
s"DB config: ${dbConfig.getConfig("bitcoin-s").asReadableJson}")
// we want to NOT resolve substitutions in the configuraton until the user
// provided configs also has been loaded. .parseResources() does not do that
// whereas .load() does
val classPathConfig = {
val applicationConf = ConfigFactory.parseResources("application.conf")
val referenceConf = ConfigFactory.parseResources("reference.conf")
applicationConf.withFallback(referenceConf)
}
logger.trace(
s"Classpath config: ${classPathConfig.getConfig("bitcoin-s").asReadableJson}")
// we want the data directory configuration
// to take preference over any bundled (classpath)
// configurations
// loads reference.conf (provided by Bitcoin-S)
val unresolvedConfig = datadirConfig
.withFallback(classPathConfig)
.withFallback(dbConfig)
logger.trace(s"Unresolved bitcoin-s config:")
logger.trace(unresolvedConfig.getConfig("bitcoin-s").asReadableJson)
val withOverrides =
if (configOverrides.nonEmpty) {
val overrides =
configOverrides
// we reverse to make the configs specified last take precedent
.reverse
.reduce(_.withFallback(_))
val interestingOverrides = overrides.getConfig("bitcoin-s")
logger.trace(
s"${configOverrides.length} user-overrides for bitcoin-s config:")
logger.trace(interestingOverrides.asReadableJson)
// to make the overrides actually override
// the default setings we have to do it
// in this order
overrides.withFallback(unresolvedConfig)
} else {
logger.trace(s"No user-provided overrides")
unresolvedConfig
}
val finalConfig = withOverrides
.resolve()
.getConfig("bitcoin-s")
val finalConfig = baseConfig.getConfig("bitcoin-s")
logger.debug(s"Resolved bitcoin-s config:")
logger.debug(finalConfig.asReadableJson)
finalConfig
}
/** The base data directory. This is where we look for a configuration file */
@ -234,7 +161,12 @@ abstract class AppConfig extends LoggerConfig with StartStopAsync[Unit] {
baseDatadir.resolve(lastDirname)
}
override val logFile: Path = datadir.resolve("bitcoin-s.log")
override val logFile: Path = {
val path = datadir.resolve("bitcoin-s.log")
// Set property for loggers
System.setProperty("bitcoins.log.location", path.toAbsolutePath.toString)
path
}
private def stringToLogLevel(str: String): Option[Level] =
str.toLowerCase() match {
@ -315,6 +247,64 @@ abstract class AppConfig extends LoggerConfig with StartStopAsync[Unit] {
object AppConfig extends BitcoinSLogger {
def getBaseConfig(
baseDatadir: Path,
configOverrides: List[Config] = List.empty): Config = {
val datadirConfig = {
val file = baseDatadir.resolve("bitcoin-s.conf")
val config = if (Files.isReadable(file)) {
ConfigFactory.parseFile(file.toFile)
} else {
ConfigFactory.empty()
}
val withDatadir =
ConfigFactory.parseString(s"bitcoin-s.datadir = $baseDatadir")
withDatadir.withFallback(config)
}
// `load` tries to resolve substitions,
// `parseResources` does not
val dbConfig = ConfigFactory
.parseResources("db.conf")
// we want to NOT resolve substitutions in the configuraton until the user
// provided configs also has been loaded. .parseResources() does not do that
// whereas .load() does
val classPathConfig = {
val applicationConf = ConfigFactory.parseResources("application.conf")
val referenceConf = ConfigFactory.parseResources("reference.conf")
applicationConf.withFallback(referenceConf)
}
// we want the data directory configuration
// to take preference over any bundled (classpath)
// configurations
// loads reference.conf (provided by Bitcoin-S)
val unresolvedConfig = datadirConfig
.withFallback(classPathConfig)
.withFallback(dbConfig)
val withOverrides =
if (configOverrides.nonEmpty) {
val overrides =
configOverrides
// we reverse to make the configs specified last take precedent
.reverse
.reduce(_.withFallback(_))
// to make the overrides actually override
// the default setings we have to do it
// in this order
overrides.withFallback(unresolvedConfig)
} else {
unresolvedConfig
}
withOverrides
.resolve()
}
/** The default data directory
*
* TODO: use different directories on Windows and Mac,

View file

@ -8,7 +8,8 @@ object Deps {
val scalacheck = "1.14.3"
val scalaTest = "3.2.0"
val scalaTestPlus = "3.2.1.0" //super annoying... https://oss.sonatype.org/content/groups/public/org/scalatestplus/
val scalaTestPlus =
"3.2.1.0" //super annoying... https://oss.sonatype.org/content/groups/public/org/scalatestplus/
val slf4j = "1.7.30"
val spray = "1.3.5"
val zeromq = "0.5.2"
@ -56,29 +57,62 @@ object Deps {
object Compile {
val bouncycastle = "org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
val scodec = "org.scodec" %% "scodec-bits" % V.scodecV withSources () withJavadoc ()
val slf4j = "org.slf4j" % "slf4j-api" % V.slf4j % "provided" withSources () withJavadoc ()
val zeromq = "org.zeromq" % "jeromq" % V.zeromq withSources () withJavadoc ()
val akkaHttp = "com.typesafe.akka" %% "akka-http" % V.akkav withSources () withJavadoc ()
val akkaStream = "com.typesafe.akka" %% "akka-stream" % V.akkaStreamv withSources () withJavadoc ()
val akkaActor = "com.typesafe.akka" %% "akka-actor" % V.akkaStreamv withSources () withJavadoc ()
val bouncycastle =
"org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
val scodec =
"org.scodec" %% "scodec-bits" % V.scodecV withSources () withJavadoc ()
val slf4j =
"org.slf4j" % "slf4j-api" % V.slf4j % "provided" withSources () withJavadoc ()
val zeromq =
"org.zeromq" % "jeromq" % V.zeromq withSources () withJavadoc ()
val akkaHttp =
"com.typesafe.akka" %% "akka-http" % V.akkav withSources () withJavadoc ()
val akkaStream =
"com.typesafe.akka" %% "akka-stream" % V.akkaStreamv withSources () withJavadoc ()
val akkaActor =
"com.typesafe.akka" %% "akka-actor" % V.akkaStreamv withSources () withJavadoc ()
val akkaSlf4j =
"com.typesafe.akka" %% "akka-slf4j" % V.akkaStreamv withSources () withJavadoc ()
val scalaFx =
"org.scalafx" %% "scalafx" % V.scalaFxV withSources () withJavadoc ()
val scalaFx = "org.scalafx" %% "scalafx" % V.scalaFxV withSources () withJavadoc ()
lazy val osName = System.getProperty("os.name") match {
case n if n.startsWith("Linux") => "linux"
case n if n.startsWith("Mac") => "mac"
case n if n.startsWith("Windows") => "win"
case _ => throw new Exception("Unknown platform!")
}
// Not sure if all of these are needed, some might be possible to remove
lazy val javaFxBase = "org.openjfx" % s"javafx-base" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxControls = "org.openjfx" % s"javafx-controls" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxFxml = "org.openjfx" % s"javafx-fxml" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxGraphics = "org.openjfx" % s"javafx-graphics" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxMedia = "org.openjfx" % s"javafx-media" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxSwing = "org.openjfx" % s"javafx-swing" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxWeb = "org.openjfx" % s"javafx-web" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxBase =
"org.openjfx" % s"javafx-base" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxControls =
"org.openjfx" % s"javafx-controls" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxFxml =
"org.openjfx" % s"javafx-fxml" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxGraphics =
"org.openjfx" % s"javafx-graphics" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxMedia =
"org.openjfx" % s"javafx-media" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxSwing =
"org.openjfx" % s"javafx-swing" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxWeb =
"org.openjfx" % s"javafx-web" % V.javaFxV classifier osName withSources () withJavadoc ()
lazy val javaFxDeps = List(javaFxBase,
javaFxControls,
javaFxFxml,
@ -87,16 +121,23 @@ object Deps {
javaFxSwing,
javaFxWeb)
val playJson = "com.typesafe.play" %% "play-json" % V.playv withSources () withJavadoc ()
val typesafeConfig = "com.typesafe" % "config" % V.typesafeConfigV withSources () withJavadoc ()
val playJson =
"com.typesafe.play" %% "play-json" % V.playv withSources () withJavadoc ()
val logback = "ch.qos.logback" % "logback-classic" % V.logback withSources () withJavadoc ()
val typesafeConfig =
"com.typesafe" % "config" % V.typesafeConfigV withSources () withJavadoc ()
val logback =
"ch.qos.logback" % "logback-classic" % V.logback withSources () withJavadoc ()
val codehaus = "org.codehaus.janino" % "janino" % V.codehausV
//for loading secp256k1 natively
val nativeLoader = "org.scijava" % "native-lib-loader" % V.nativeLoaderV withSources () withJavadoc ()
val nativeLoader =
"org.scijava" % "native-lib-loader" % V.nativeLoaderV withSources () withJavadoc ()
//node deps
val slick = "com.typesafe.slick" %% "slick" % V.slickV withSources () withJavadoc ()
val slick =
"com.typesafe.slick" %% "slick" % V.slickV withSources () withJavadoc ()
val slickHikari = "com.typesafe.slick" %% "slick-hikaricp" % V.slickV
val sqlite = "org.xerial" % "sqlite-jdbc" % V.sqliteV
val postgres = "org.postgresql" % "postgresql" % V.postgresV
@ -115,26 +156,49 @@ object Deps {
// HTTP client lib
val sttp = "com.softwaremill.sttp" %% "core" % V.sttpV
val scalacheck = "org.scalacheck" %% "scalacheck" % V.scalacheck withSources () withJavadoc ()
val scalaTest = "org.scalatest" %% "scalatest" % V.scalaTest withSources () withJavadoc ()
val scalaTestPlus = "org.scalatestplus" %% "scalacheck-1-14" % V.scalaTestPlus withSources () withJavadoc ()
val pgEmbedded = "com.opentable.components" % "otj-pg-embedded" % V.pgEmbeddedV withSources () withJavadoc ()
val scalacheck =
"org.scalacheck" %% "scalacheck" % V.scalacheck withSources () withJavadoc ()
val scalaTest =
"org.scalatest" %% "scalatest" % V.scalaTest withSources () withJavadoc ()
val scalaTestPlus =
"org.scalatestplus" %% "scalacheck-1-14" % V.scalaTestPlus withSources () withJavadoc ()
val pgEmbedded =
"com.opentable.components" % "otj-pg-embedded" % V.pgEmbeddedV withSources () withJavadoc ()
}
object Test {
val newAsync = "org.scala-lang.modules" %% "scala-async" % V.asyncNewScalaV % "test" withSources () withJavadoc ()
val junitInterface = "com.novocode" % "junit-interface" % V.junitV % "test" withSources () withJavadoc ()
val newAsync =
"org.scala-lang.modules" %% "scala-async" % V.asyncNewScalaV % "test" withSources () withJavadoc ()
val junitInterface =
"com.novocode" % "junit-interface" % V.junitV % "test" withSources () withJavadoc ()
val logback = Compile.logback % "test"
val scalacheck = Compile.scalacheck % "test"
val scalaTest = Compile.scalaTest % "test"
val scalaMock = "org.scalamock" %% "scalamock" % V.scalamockV
val spray = "io.spray" %% "spray-json" % V.spray % "test" withSources () withJavadoc ()
val akkaHttp = "com.typesafe.akka" %% "akka-http-testkit" % V.akkav % "test" withSources () withJavadoc ()
val akkaStream = "com.typesafe.akka" %% "akka-stream-testkit" % V.akkaStreamv % "test" withSources () withJavadoc ()
val spray =
"io.spray" %% "spray-json" % V.spray % "test" withSources () withJavadoc ()
val akkaHttp =
"com.typesafe.akka" %% "akka-http-testkit" % V.akkav % "test" withSources () withJavadoc ()
val akkaStream =
"com.typesafe.akka" %% "akka-stream-testkit" % V.akkaStreamv % "test" withSources () withJavadoc ()
val playJson = Compile.playJson % "test"
val akkaTestkit = "com.typesafe.akka" %% "akka-testkit" % V.akkaActorV withSources () withJavadoc ()
val scalameter = "com.storm-enroute" %% "scalameter" % V.scalameterV % "test" withSources () withJavadoc ()
val pgEmbedded = "com.opentable.components" % "otj-pg-embedded" % V.pgEmbeddedV % "test" withSources () withJavadoc ()
val akkaTestkit =
"com.typesafe.akka" %% "akka-testkit" % V.akkaActorV withSources () withJavadoc ()
val scalameter =
"com.storm-enroute" %% "scalameter" % V.scalameterV % "test" withSources () withJavadoc ()
val pgEmbedded =
"com.opentable.components" % "otj-pg-embedded" % V.pgEmbeddedV % "test" withSources () withJavadoc ()
}
val chain = List(
@ -145,11 +209,12 @@ object Deps {
Test.pgEmbedded
)
def appCommons(scalaVersion: String) = List(
Compile.newMicroPickle,
Compile.playJson,
Compile.slf4j
)
def appCommons(scalaVersion: String) =
List(
Compile.newMicroPickle,
Compile.playJson,
Compile.slf4j
)
val core = List(
Compile.bouncycastle,
@ -193,14 +258,15 @@ object Deps {
Compile.typesafeConfig
)
def bitcoindRpcTest(scalaVersion: String) = List(
Test.akkaHttp,
Test.akkaStream,
Test.logback,
Test.scalaTest,
Test.scalacheck,
Test.newAsync
)
def bitcoindRpcTest(scalaVersion: String) =
List(
Test.akkaHttp,
Test.akkaStream,
Test.logback,
Test.scalaTest,
Test.scalacheck,
Test.newAsync
)
val bench = List(
"org.slf4j" % "slf4j-api" % V.slf4j withSources () withJavadoc (),
@ -219,25 +285,28 @@ object Deps {
Test.pgEmbedded
)
def cli(scalaVersion: String) = List(
Compile.sttp,
Compile.newMicroPickle,
Compile.logback,
Compile.scopt,
//we can remove this dependency when this is fixed
//https://github.com/oracle/graal/issues/1943
//see https://github.com/bitcoin-s/bitcoin-s/issues/1100
Compile.codehaus
)
def cli(scalaVersion: String) =
List(
Compile.sttp,
Compile.newMicroPickle,
Compile.logback,
Compile.scopt,
//we can remove this dependency when this is fixed
//https://github.com/oracle/graal/issues/1943
//see https://github.com/bitcoin-s/bitcoin-s/issues/1100
Compile.codehaus
)
val gui = List(Compile.scalaFx) ++ Compile.javaFxDeps
def server(scalaVersion: String) = List(
Compile.newMicroPickle,
Compile.logback,
Compile.akkaActor,
Compile.akkaHttp
)
def server(scalaVersion: String) =
List(
Compile.newMicroPickle,
Compile.logback,
Compile.akkaActor,
Compile.akkaHttp,
Compile.akkaSlf4j
)
val eclairRpc = List(
Compile.akkaHttp,
@ -288,19 +357,21 @@ object Deps {
Test.akkaTestkit
)
def keyManager(scalaVersion: String) = List(
Compile.newMicroJson
)
def keyManager(scalaVersion: String) =
List(
Compile.newMicroJson
)
val keyManagerTest = List(
Compile.slf4j,
Test.logback
)
def wallet(scalaVersion: String) = List(
Compile.newMicroJson,
Compile.logback
)
def wallet(scalaVersion: String) =
List(
Compile.newMicroJson,
Compile.logback
)
val walletTest = List(
Test.akkaTestkit,