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
|
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) = ()
}
class CallbackStream(wrapped: OutputStream, f: () => Unit) extends OutputStream{
override def write(b: Array[Byte]): Unit = { f(); wrapped.write(b) }
override def write(b: Array[Byte], off: Int, len: Int): Unit = {
f()
wrapped.write(b, off, len)
}
def write(b: Int) = {f(); wrapped.write(b)}
}
case class PrintLogger(colored: Boolean,
colors: ammonite.util.Colors,
outStream: PrintStream,
infoStream: PrintStream,
errStream: PrintStream) extends Logger {
var lastLineTicker = false
def falseTicker[T](t: T) = {
lastLineTicker = false
t
}
override val errorStream = new PrintStream(
new CallbackStream(errStream, () => lastLineTicker = false)
)
override val outputStream = new PrintStream(
new CallbackStream(outStream, () => lastLineTicker = false)
)
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())
}
|