1 /**
2  * Copyright: Copyright Jason White, 2016
3  * License:   MIT
4  * Authors:   Jason White
5  *
6  * Description:
7  * General logging of events to the console.
8  */
9 module button.loggers.console;
10 
11 import core.time : Duration;
12 
13 import io.file.stream;
14 import io.text;
15 
16 import button.events;
17 import button.task;
18 import button.state;
19 import button.textcolor : TextColor;
20 
21 final class ConsoleLogger : Events
22 {
23     private
24     {
25         import std.range : Appender;
26 
27         // Console streams to write to.
28         File stdout;
29         File stderr;
30 
31         // True if output should be verbose.
32         bool verbose;
33 
34         TextColor color;
35 
36         // List of current task output. There is one appender per worker in the
37         // thread pool.
38         Appender!(ubyte[])[] output;
39     }
40 
41     this(File stdout, File stderr, bool verbose, size_t poolSize)
42     {
43         this.stdout = stdout;
44         this.stderr = stderr;
45         this.verbose = verbose;
46         this.color = TextColor(true);
47 
48         // The +1 is to accommodate index 0 which is used for threads not in the
49         // pool.
50         this.output.length = poolSize + 1;
51     }
52 
53     void buildStarted()
54     {
55     }
56 
57     void buildSucceeded(Duration duration)
58     {
59     }
60 
61     void buildFailed(Duration duration, Exception e)
62     {
63     }
64 
65     void taskStarted(size_t worker, const ref Task task)
66     {
67         output[worker].clear();
68     }
69 
70     private void printTaskOutput(size_t worker)
71     {
72         auto data = output[worker].data;
73 
74         stdout.write(data);
75 
76         if (data.length > 0 && data[$-1] != '\n')
77             stdout.print("⏎\n");
78     }
79 
80     private void printTaskTail(size_t worker, Duration duration)
81     {
82         import core.time : Duration;
83 
84         if (verbose)
85         {
86             stdout.println(color.status, "   ➥ Time taken: ", color.reset,
87                     cast(Duration)duration);
88         }
89     }
90 
91     void taskSucceeded(size_t worker, const ref Task task,
92             Duration duration)
93     {
94         synchronized (this)
95         {
96             stdout.println(color.status, " > ", color.reset,
97                     task.toPrettyString(verbose));
98 
99             printTaskOutput(worker);
100             printTaskTail(worker, duration);
101         }
102     }
103 
104     void taskFailed(size_t worker, const ref Task task, Duration duration,
105             const Exception e)
106     {
107         import std..string : wrap;
108 
109         synchronized (this)
110         {
111             stdout.println(color.status, " > ", color.error,
112                     task.toPrettyString(verbose), color.reset);
113 
114             printTaskOutput(worker);
115             printTaskTail(worker, duration);
116 
117             enum indent = "             ";
118 
119             stdout.print(color.status, "   ➥ ", color.error, "Error: ",
120                     color.reset, wrap(e.msg, 80, "", indent, 4));
121         }
122     }
123 
124     void taskOutput(size_t worker, in ubyte[] chunk)
125     {
126         output[worker].put(chunk);
127     }
128 }