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 cstring 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 istring Eol = "\n";
148 
149         with ( Terminal )
150         {
151             layout.format(
152               event,
153               ( cstring content )
154               {
155                   size_t written;
156                   while (pos + content.length > buffer.length)
157                   {
158                       buffer[pos .. $] = content[0 .. buffer.length - pos];
159 
160                       written += stream_.write(CSI);
161                       written += stream_.write(LINE_UP);
162 
163                       written += stream_.write(CSI);
164                       written += stream_.write(SCROLL_UP);
165 
166                       written += stream_.write(CSI);
167                       written += stream_.write(INSERT_LINE);
168 
169                       written += stream_.write(buffer);
170 
171                       stream_.write(Eol);
172                       stream_.flush;
173                       buffer[] = '\0';
174                       content = content[buffer.length - pos .. $];
175 
176                       pos = 0;
177                   }
178 
179                   if (content.length > 0)
180                   {
181                       buffer[pos .. pos + content.length] = content[];
182                       pos += content.length;
183                   }
184               } );
185 
186             stream_.write(CSI);
187             stream_.write(LINE_UP);
188 
189             stream_.write(CSI);
190             stream_.write(SCROLL_UP);
191 
192             stream_.write(CSI);
193             stream_.write(INSERT_LINE);
194 
195             stream_.write(buffer);
196             stream_.flush;
197 
198             pos = 0;
199             buffer[] = '\0';
200 
201             stream_.write(Eol);
202 
203             if (flush_) stream_.flush;
204         }
205     }
206 }