summaryrefslogtreecommitdiff
path: root/core/src/main/scala/mill/util/Logger.scala
blob: 316e17ff1359adc8c3947cc5916b6ec2cde05a86 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package mill.util

import java.io._

import ammonite.ops.Path
import ammonite.util.Colors


/**
  * The standard logging interface of the Mill build tool.
  *
  * Contains four primary logging methods, in order of increasing importance:
  *
  * - `ticker`: short-lived logging output where consecutive lines over-write
  *   each other; useful for information which is transient and disposable
  *
  * - `info`: miscellaneous logging output which isn't part of the main output
  *   a user is looking for, but useful to provide context on what Mill is doing
  *
  * - `error`: logging output which represents problems the user should care
  *   about
  *
  * Also contains the two forwarded stdout and stderr streams, for code executed
  * by Mill to use directly. Typically these correspond to the stdout and stderr,
  * but when `--show` is used both are forwarded to stderr and stdout is only
  * used to display the final `--show` output for easy piping.
  */
trait Logger {
  def colored: Boolean
  val errorStream: PrintStream
  val outputStream: PrintStream
  def info(s: String): Unit
  def error(s: String): Unit
  def ticker(s: String): Unit
  def close(): Unit = ()
}

object DummyLogger extends Logger {
  def colored = false
  object errorStream extends PrintStream(_ => ())
  object outputStream extends PrintStream(_ => ())
  def info(s: String) = ()
  def error(s: String) = ()
  def ticker(s: String) = ()
}

case class PrintLogger(colored: Boolean,
                       colors: ammonite.util.Colors,
                       outStream: PrintStream,
                       infoStream: PrintStream,
                       errStream: PrintStream) extends Logger {

  var lastLineTicker = false
  override val errorStream = new PrintStream(
    new OutputStream {
      override def write(b: Array[Byte]): Unit = {
        lastLineTicker = false
        errStream.write(b)
      }

      override def write(b: Array[Byte], off: Int, len: Int): Unit = {
        lastLineTicker = false
        errStream.write(b, off, len)
      }

      def write(b: Int) = {
        lastLineTicker = false
        errStream.write(b)
      }
    }
  )
  override val outputStream = new PrintStream(
    new OutputStream {
      override def write(b: Array[Byte]): Unit = {
        lastLineTicker = false
        outStream.write(b)
      }

      override def write(b: Array[Byte], off: Int, len: Int): Unit = {
        lastLineTicker = false
        outStream.write(b, off, len)
      }

      def write(b: Int) = {
        lastLineTicker = false
        outStream.write(b)
      }
    }
  )


  def info(s: String) = {
    lastLineTicker = false
    infoStream.println(colors.info()(s))
  }
  def error(s: String) = {
    lastLineTicker = false
    errStream.println(colors.error()(s))
  }
  def ticker(s: String) = {
    if (lastLineTicker){
      val p = new PrintWriter(infoStream)
      val nav = new ammonite.terminal.AnsiNav(p)
      nav.up(1)
      nav.clearLine(2)
      nav.left(9999)
      p.flush()
    }
    lastLineTicker = true
    infoStream.println(colors.info()(s))
  }
}

case class FileLogger(colored: Boolean, file: Path) extends Logger {
  private[this] var outputStreamUsed: Boolean = false

  lazy val outputStream = {
    outputStreamUsed = true
    new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath))
  }

  lazy val errorStream = {
    outputStreamUsed = true
    new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath))
  }

  def info(s: String) = outputStream.println(s)
  def error(s: String) = outputStream.println(s)
  def ticker(s: String) = outputStream.println(s)
  override def close() = {
    if (outputStreamUsed)
      outputStream.close()
  }
}

case class MultiLogger(colored: Boolean, streams: Logger*) extends Logger {
  lazy val outputStream: PrintStream =
    new PrintStream(b => streams.foreach(_.outputStream.write(b))) {
      override def flush() = streams.foreach(_.outputStream.flush())
      override def close() = streams.foreach(_.outputStream.close())
    }
  lazy val errorStream: PrintStream =
    new PrintStream(b => streams.foreach(_.outputStream.write(b))) {
      override def flush() = streams.foreach(_.outputStream.flush())
      override def close() = streams.foreach(_.outputStream.close())
    }

  def info(s: String) = streams.foreach(_.info(s))
  def error(s: String) = streams.foreach(_.error(s))
  def ticker(s: String) = streams.foreach(_.ticker(s))
  override def close() = streams.foreach(_.close())
}