1 /******************************************************************************* 2 3 InsertConsole 4 5 An appender for the tango logger which writes the output _above_ the 6 current cursor position, breaking the line automatically. 7 8 This appender was developed in order to allow applications using 9 ocean.io.console.AppStatus to split the output console to send logs to the 10 top streaming portion without affecting the bottom static portion. For more 11 details, please refer to the documentation in the AppStatus module. 12 13 Copyright: 14 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 15 All rights reserved. 16 17 License: 18 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 19 Alternatively, this file may be distributed under the terms of the Tango 20 3-Clause BSD License (see LICENSE_BSD.txt for details). 21 22 *******************************************************************************/ 23 24 module ocean.util.log.InsertConsole; 25 26 27 import ocean.meta.types.Qualifiers; 28 import ocean.core.Verify; 29 30 import ocean.io.Terminal; 31 32 import ocean.io.Console; 33 34 import Integer = ocean.text.convert.Integer_tango; 35 36 import ocean.io.model.IConduit; 37 import ocean.util.log.Appender; 38 import ocean.util.log.Event; 39 40 import core.sys.posix.signal; 41 42 43 /******************************************************************************* 44 45 An appender for the tango logger which writes the output _above_ the 46 current cursor position, breaking the line automatically 47 48 This was copied from ocean.util.log.AppendConsole and modified 49 50 *******************************************************************************/ 51 52 public class InsertConsole: Appender 53 { 54 private Mask mask_; 55 private bool flush_; 56 private OutputStream stream_; 57 58 private char[] buffer; 59 60 /*********************************************************************** 61 62 Create with the given layout 63 64 ***********************************************************************/ 65 66 this ( Appender.Layout how = null ) 67 { 68 this(Cerr.stream, true, how); 69 } 70 71 /*********************************************************************** 72 73 Create with the given stream and layout 74 75 ***********************************************************************/ 76 77 this ( OutputStream stream, bool flush = false, Appender.Layout how = null ) 78 { 79 verify (stream !is null); 80 81 mask_ = register(name ~ stream.classinfo.name); 82 this.connectOutput(stream); 83 flush_ = flush; 84 layout(how); 85 86 this.buffer = new char[Terminal.columns]; 87 this.buffer[] = '\0'; 88 } 89 90 /*********************************************************************** 91 92 Sets the output stream to the different stream. 93 94 Params: 95 output = stream to output to. 96 97 ***********************************************************************/ 98 99 void connectOutput ( OutputStream stream ) 100 { 101 this.stream_ = stream; 102 } 103 104 /*********************************************************************** 105 106 Return the fingerprint for this class 107 108 ***********************************************************************/ 109 110 final override Mask mask ( ) 111 { 112 return mask_; 113 } 114 115 /*********************************************************************** 116 117 Return the name of this class 118 119 ***********************************************************************/ 120 121 override string name ( ) 122 { 123 return this.classinfo.name; 124 } 125 126 /*********************************************************************** 127 128 Append an event to the output. 129 130 ***********************************************************************/ 131 132 final override void append ( LogEvent event ) 133 { 134 // attempt to format output for non-existing terminal will cause 135 // an infinite loop 136 if (!Terminal.columns) 137 return; 138 139 if (this.buffer.length != Terminal.columns) 140 { 141 this.buffer.length = Terminal.columns; 142 buffer[] = '\0'; 143 } 144 145 ushort pos = 0; 146 147 static immutable string Eol = "\n"; 148 149 with ( Terminal ) 150 { 151 layout.format( 152 event, 153 (in cstring content_) 154 { 155 size_t written; 156 scope const(char)[] content = content_; 157 while (pos + content.length > buffer.length) 158 { 159 buffer[pos .. $] = content[0 .. buffer.length - pos]; 160 161 written += stream_.write(CSI); 162 written += stream_.write(LINE_UP); 163 164 written += stream_.write(CSI); 165 written += stream_.write(SCROLL_UP); 166 167 written += stream_.write(CSI); 168 written += stream_.write(INSERT_LINE); 169 170 written += stream_.write(buffer); 171 172 stream_.write(Eol); 173 stream_.flush; 174 buffer[] = '\0'; 175 content = content[buffer.length - pos .. $]; 176 177 pos = 0; 178 } 179 180 if (content.length > 0) 181 { 182 buffer[pos .. pos + content.length] = content[]; 183 pos += content.length; 184 } 185 } ); 186 187 stream_.write(CSI); 188 stream_.write(LINE_UP); 189 190 stream_.write(CSI); 191 stream_.write(SCROLL_UP); 192 193 stream_.write(CSI); 194 stream_.write(INSERT_LINE); 195 196 stream_.write(buffer); 197 stream_.flush; 198 199 pos = 0; 200 buffer[] = '\0'; 201 202 stream_.write(Eol); 203 204 if (flush_) stream_.flush; 205 } 206 } 207 }