1 /**
2  * Copyright: Copyright Jason White, 2016
3  * License:   MIT
4  * Authors:   Jason White
5  *
6  * Description:
7  * General logging of events.
8  */
9 module button.log.file;
10 
11 import core.time : TickDuration;
12 
13 import button.log;
14 import button.task;
15 import button.state;
16 
17 import io.file.stream;
18 import io.text;
19 
20 final class FileLogger : Logger
21 {
22     import button.textcolor : TextColor;
23 
24     private
25     {
26         // File stream to log to
27         File file;
28 
29         // True if output should be verbose.
30         bool verbose;
31 
32         TextColor color;
33     }
34 
35     private final class FileTaskLogger : TaskLogger
36     {
37         private
38         {
39             import std.range : appender, Appender;
40 
41             Task task;
42             Appender!(ubyte[]) _output;
43         }
44 
45         this(Task task)
46         {
47             this.task = task;
48             _output = appender!(ubyte[]);
49         }
50 
51         void output(in ubyte[] chunk)
52         {
53             _output.put(chunk);
54         }
55 
56         private void printOutput()
57         {
58             file.write(_output.data);
59 
60             // Ensure there is always a line separator after the output
61             if (_output.data.length > 0 && _output.data[$-1] != '\n')
62                 file.print("⏎\n");
63         }
64 
65         private void printTail(TickDuration duration)
66         {
67             import core.time : Duration;
68             if (verbose)
69                 file.println(color.status, "   ➥ Time taken: ", color.reset,
70                         cast(Duration)duration);
71         }
72 
73         void succeeded(TickDuration duration)
74         {
75             synchronized (this.outer)
76             {
77                 file.println(color.status, " > ", color.reset,
78                         task.toPrettyString(verbose));
79 
80                 printOutput();
81                 printTail(duration);
82             }
83         }
84 
85         void failed(TickDuration duration, Exception e)
86         {
87             import std.string : wrap;
88 
89             synchronized (this.outer)
90             {
91                 file.println(color.status, " > ", color.error,
92                         task.toPrettyString(verbose), color.reset);
93 
94                 printOutput();
95                 printTail(duration);
96 
97                 enum indent = "             ";
98 
99                 file.print(color.status, "   ➥ ", color.error, "Error: ",
100                         color.reset, wrap(e.msg, 80, "", indent, 4));
101             }
102         }
103     }
104 
105     this(File file, bool verbose)
106     {
107         this.file = file;
108         this.verbose = verbose;
109         this.color = TextColor(true);
110     }
111 
112     void buildStarted()
113     {
114     }
115 
116     void buildEnded(bool success, TickDuration duration)
117     {
118     }
119 
120     TaskLogger taskStarted(Index!Task index, Task task, bool dryRun)
121     {
122         return new FileTaskLogger(task);
123     }
124 }