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 }