aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChick Markley2016-10-25 00:23:32 -0700
committerGitHub2016-10-25 00:23:32 -0700
commitd344f4400ad5e9c71c97229e33660bbe067260a0 (patch)
treee0426f4c9cc608db5c5f3a6a81f3873197424511 /src
parentaca23a617999287183effea0272a4c95b27ff4b2 (diff)
Logger 1 (#338)
* Create a simple system for executions and command line parameters New model for tracking parameters and having those parameters register scopt command to allow the parameters to be set by command line args. Create composable forms of the these parameters to allow separate elements of the chisel3 toolchain to combine these parameters Create execution return structures that simplify return values to earlier toolchain elements * just a little bit of cleanup * Fixes for Adam's comments on PR * knuckled under to self-pressure to allow former -i and -o to work * knuckled under to self-pressure to allow former -i and -o to work * show defaults for command line args with them * A couple of fixes from merging latest master * Implement a log4scala like logging system This system has the rather remarkable property that it is possible to turn it on conveniently when you want it. It also provides for class level granularity as well as the traditional Error, Warn, Info, Debug * some style fixes and change infoMode default to append per PR #328 * some style fixes and change infoMode default to append per PR #328 * support -i -o and -X a couple of indentation and spacing fixes
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Driver.scala4
-rw-r--r--src/main/scala/firrtl/ExecutionOptionsManager.scala80
-rw-r--r--src/main/scala/firrtl/LoweringCompilers.scala2
-rw-r--r--src/main/scala/logger/Logger.scala101
4 files changed, 182 insertions, 5 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala
index 5dbbf16a..ba5527b4 100644
--- a/src/main/scala/firrtl/Driver.scala
+++ b/src/main/scala/firrtl/Driver.scala
@@ -4,6 +4,8 @@ package firrtl
import java.io.FileNotFoundException
+import logger.Logger
+
import scala.io.Source
import Annotations._
@@ -74,6 +76,8 @@ object Driver {
def execute(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): FirrtlExecutionResult = {
val firrtlConfig = optionsManager.firrtlOptions
+ Logger.setOptions(optionsManager)
+
val firrtlSource = firrtlConfig.firrtlSource match {
case Some(text) => text.split("\n").toIterator
case None =>
diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala
index 05a9f487..ff725e30 100644
--- a/src/main/scala/firrtl/ExecutionOptionsManager.scala
+++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala
@@ -5,6 +5,7 @@ package firrtl
import firrtl.Annotations._
import firrtl.Parser._
import firrtl.passes.memlib.ReplSeqMemAnnotation
+import logger.LogLevel
import scopt.OptionParser
import scala.collection.Seq
@@ -15,6 +16,10 @@ import scala.collection.Seq
*/
trait ComposableOptions
+abstract class HasParser(applicationName: String) {
+ final val parser: OptionParser[Unit] = new OptionParser[Unit](applicationName) {}
+}
+
/**
* Most of the chisel toolchain components require a topName which defines a circuit or a device under test.
* Much of the work that is done takes place in a directory.
@@ -22,10 +27,22 @@ trait ComposableOptions
* For example, in chisel, by deferring this it is possible for the execute there to first elaborate the
* circuit and then set the topName from that if it has not already been set.
*/
-case class CommonOptions(topName: String = "", targetDirName: String = "test_run_dir") extends ComposableOptions
-
-abstract class HasParser(applicationName: String) {
- final val parser: OptionParser[Unit] = new OptionParser[Unit](applicationName) {}
+case class CommonOptions(
+ topName: String = "",
+ targetDirName: String = "test_run_dir",
+ globalLogLevel: LogLevel.Value = LogLevel.Error,
+ logToFile: Boolean = false,
+ logClassNames: Boolean = false,
+ classLogLevels: Map[String, LogLevel.Value] = Map.empty) extends ComposableOptions {
+
+ def getLogFileName(optionsManager: ExecutionOptionsManager): String = {
+ if(topName.isEmpty) {
+ optionsManager.getBuildFileName("log", "firrtl")
+ }
+ else {
+ optionsManager.getBuildFileName("log")
+ }
+ }
}
trait HasCommonOptions {
@@ -49,6 +66,61 @@ trait HasCommonOptions {
}
.text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+ parser.opt[String]("log-level")
+ .abbr("ll").valueName("<Error|Warn|Info|Debug|Trace>")
+ .foreach { x =>
+ val level = x.toLowerCase match {
+ case "error" => LogLevel.Error
+ case "warn" => LogLevel.Warn
+ case "info" => LogLevel.Info
+ case "debug" => LogLevel.Debug
+ case "trace" => LogLevel.Trace
+ }
+ commonOptions = commonOptions.copy(globalLogLevel = level)
+ }
+ .validate { x =>
+ if (Array("error", "warn", "info", "debug", "trace").contains(x.toLowerCase)) parser.success
+ else parser.failure(s"$x bad value must be one of ignore|use|gen|append")
+ }
+ .text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+
+ parser.opt[Seq[String]]("class-log-level")
+ .abbr("cll").valueName("<FullClassName:[Error|Warn|Info|Debug|Trace]>[,...]")
+ .foreach { x =>
+ val logAssignments = x.map { y =>
+ val className :: levelName :: _ = y.split(":").toList
+
+ val level = levelName.toLowerCase match {
+ case "error" => LogLevel.Error
+ case "warn" => LogLevel.Warn
+ case "info" => LogLevel.Info
+ case "debug" => LogLevel.Debug
+ case "trace" => LogLevel.Trace
+ case _ =>
+ throw new Exception(s"Error: bad command line arguments for --class-log-level $x")
+ }
+ className -> level
+ }
+
+ commonOptions = commonOptions.copy(classLogLevels = commonOptions.classLogLevels ++ logAssignments)
+
+ }
+ .text(s"This options defines a work directory for intermediate files, default is ${commonOptions.targetDirName}")
+
+ parser.opt[Unit]("log-to-file")
+ .abbr("ltf")
+ .foreach { _ =>
+ commonOptions = commonOptions.copy(logToFile = true)
+ }
+ .text(s"default logs to stdout, this flags writes to topName.log or firrtl.log if no topName")
+
+ parser.opt[Unit]("log-class-names")
+ .abbr("lcn")
+ .foreach { _ =>
+ commonOptions = commonOptions.copy(logClassNames = true)
+ }
+ .text(s"shows class names and log level in logging output, useful for target --class-log-level")
+
parser.help("help").text("prints this usage text")
}
diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala
index 53491922..33553179 100644
--- a/src/main/scala/firrtl/LoweringCompilers.scala
+++ b/src/main/scala/firrtl/LoweringCompilers.scala
@@ -27,11 +27,11 @@ MODIFICATIONS.
package firrtl
-import com.typesafe.scalalogging.LazyLogging
import java.io.Writer
import firrtl.passes.Pass
import firrtl.ir.Circuit
import Annotations._
+import logger.LazyLogging
// ===========================================
// Utility Traits
diff --git a/src/main/scala/logger/Logger.scala b/src/main/scala/logger/Logger.scala
new file mode 100644
index 00000000..07855c47
--- /dev/null
+++ b/src/main/scala/logger/Logger.scala
@@ -0,0 +1,101 @@
+// See LICENSE for license details.
+
+package logger
+
+import java.io.{PrintStream, File, FileOutputStream}
+
+import firrtl.ExecutionOptionsManager
+
+/**
+ * This provides a facility for a log4scala* type logging system. Why did we write our own? Because
+ * the canned ones are just to darned hard to turn on, particularly when embedded in a distribution.
+ * This one can be turned on programmatically or with the options exposed in the [[firrtl.CommonOptions]]
+ * and [[ExecutionOptionsManager]] API's in firrtl.
+ * There are 4 main options.
+ * * a simple global option to turn on all in scope (and across threads, might want to fix this)
+ * * turn on specific levels for specific fully qualified class names
+ * * set a file to write things to, default is just to use stdout
+ * * include the class names and level in the output. This is useful to figure out what
+ * the class names that extend LazyLogger are
+ *
+ * This is not overly optimized but does pass the string as () => String to avoid string interpolation
+ * occurring if the the logging level is not sufficiently high. This could be further optimized by playing
+ * with methods
+ */
+/**
+ * The supported log levels, what do they mean? Whatever you want them to.
+ */
+object LogLevel extends Enumeration {
+ val Error, Warn, Info, Debug, Trace = Value
+}
+
+/**
+ * extend this trait to enable logging in a class you are implementing
+ */
+trait LazyLogging {
+ val logger = new Logger(this.getClass.getName)
+}
+
+/**
+ * Singleton in control of what is supposed to get logged, how it's to be logged and where it is to be logged
+ */
+object Logger {
+ var globalLevel = LogLevel.Error
+ val classLevels = new scala.collection.mutable.HashMap[String, LogLevel.Value]
+ var logClassNames = false
+
+ def showMessage(level: LogLevel.Value, className: String, message: => String): Unit = {
+ if(globalLevel == level || (classLevels.nonEmpty && classLevels.getOrElse(className, LogLevel.Error) >= level)) {
+ if(logClassNames) {
+ stream.println(s"[$level:$className] $message")
+ }
+ else {
+ stream.println(message)
+ }
+ }
+ }
+
+ var stream: PrintStream = System.out
+
+ def setOutput(fileName: String): Unit = {
+ stream = new PrintStream(new FileOutputStream(new File(fileName)))
+ }
+ def setConsole(): Unit = {
+ stream = Console.out
+ }
+ def setClassLogLevels(namesToLevel: Map[String, LogLevel.Value]): Unit = {
+ classLevels ++= namesToLevel
+ }
+
+ def setOptions(optionsManager: ExecutionOptionsManager): Unit = {
+ val commonOptions = optionsManager.commonOptions
+ globalLevel = commonOptions.globalLogLevel
+ setClassLogLevels(commonOptions.classLogLevels)
+ if(commonOptions.logToFile) {
+ setOutput(commonOptions.getLogFileName(optionsManager))
+ }
+ logClassNames = commonOptions.logClassNames
+ }
+}
+
+/**
+ * Classes implementing [[LazyLogging]] will have logger of this type
+ * @param containerClass passed in from the LazyLogging trait in order to provide class level logging granularity
+ */
+class Logger(containerClass: String) {
+ def error(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Error, containerClass, message)
+ }
+ def warn(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Warn, containerClass, message)
+ }
+ def info(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Info, containerClass, message)
+ }
+ def debug(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Debug, containerClass, message)
+ }
+ def trace(message: => String): Unit = {
+ Logger.showMessage(LogLevel.Trace, containerClass, message)
+ }
+}