1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6       
7         version:        Initial release: May 2004
8         
9         author:         Kris
10 
11 *******************************************************************************/
12 
13 module tango.util.log.AppendFiles;
14 
15 private import  tango.time.Time;
16 
17 private import  Path = tango.io.Path,
18                 tango.io.device.File;
19 
20 private import  tango.io.model.IFile;
21 
22 private import  tango.util.log.Log,
23                 tango.util.log.AppendFile;
24 
25 /*******************************************************************************
26 
27         Append log messages to a file set 
28 
29 *******************************************************************************/
30 
31 public class AppendFiles : Filer
32 {
33         private Mask            mask_;
34         private const(char)[][]        paths;
35         private int             index;
36         private long            maxSize,
37                                 fileSize;
38 
39         /***********************************************************************
40                 
41                 Create an AppendFiles upon a file-set with the specified 
42                 path and optional layout. The minimal file count is two 
43                 and the maximum is 1000 (explicitly 999). Note that files
44                 are numbered starting with zero rather than one.
45 
46                 A path of "my.log" will be expanded to "my.0.log".
47 
48                 maxSize is the advisory maximum size of a single log-file,
49                 in bytes.
50 
51                 Where a file set already exists, we resume appending to 
52                 the one with the most recent activity timestamp
53 
54         ***********************************************************************/
55 
56         this (const(char)[] path, int count, long maxSize, Appender.Layout how = null)
57         {
58                 --count;
59                 assert (path);
60                 assert (count > 0 && count < 1000);
61 
62                 // Get a unique fingerprint for this instance
63                 mask_ = register (path);
64 
65                 // split the path into components
66                 auto c = Path.parse (path);
67 
68                 char[3] x;
69                 Time mostRecent;
70                 for (int i=0; i <= count; ++i)
71                     {
72                     x[0] = cast(char)('0' + i/100);
73                     x[1] = cast(char)('0' + i/10%10);
74                     x[2] = cast(char)('0' + i%10);
75                     auto p = c.toString()[0..$-c.suffix.length] ~ x ~ c.suffix;
76                     paths ~= p;
77 
78                     // use the most recent file in the set
79                     if (Path.exists(p))
80                        {
81                        auto modified = Path.modified(p);
82                        if (modified > mostRecent)
83                           {
84                           mostRecent = modified;
85                           index = i;
86                           }
87                        }
88                     }
89 
90                 // remember the maximum size 
91                 this.maxSize = maxSize;
92 
93                 // adjust index and open the appropriate log file
94                 --index; 
95                 nextFile (false);
96 
97                 // set provided layout (ignored when null)
98                 layout (how);
99         }
100 
101         /***********************************************************************
102                 
103                 Return the fingerprint for this class
104 
105         ***********************************************************************/
106 
107         @property override final const Mask mask ()
108         {
109                 return mask_;
110         }
111 
112         /***********************************************************************
113                 
114                 Return the name of this class
115 
116         ***********************************************************************/
117 
118         @property override final const const(char)[] name ()
119         {
120                 return this.classinfo.name;
121         }
122 
123         /***********************************************************************
124                 
125                 Append an event to the output
126                  
127         ***********************************************************************/
128 
129         override final void append (LogEvent event)
130         {
131                 synchronized(this)
132                 {
133                     char[] msg;
134 
135                     // file already full?
136                     if (fileSize >= maxSize)
137                         nextFile (true);
138 
139                     size_t write (const(void)[] content)
140                     {
141                             fileSize += content.length;
142                             return buffer.write (content);
143                     }
144 
145                     // write log message and flush it
146                     layout.format (event, &write);
147                     write (FileConst.NewlineString);
148                     buffer.flush(); 
149                 }
150         }
151 
152         /***********************************************************************
153                 
154                 Switch to the next file within the set
155 
156         ***********************************************************************/
157 
158         private void nextFile (bool reset)
159         {
160                 // select next file in the set
161                 if (++index >= paths.length)
162                     index = 0;
163                 
164                 // close any existing conduit
165                 close();
166 
167                 // make it shareable for read
168                 auto style = File.WriteAppending;
169                 style.share = File.Share.Read;
170                 auto conduit = new File (paths[index], style);
171 
172                 configure (conduit);
173 
174                 // reset file size
175                 if (reset)
176                     conduit.truncate (fileSize = 0);
177                 else
178                    fileSize = conduit.length;
179         }
180 }
181 
182 /*******************************************************************************
183 
184 *******************************************************************************/
185 
186 debug (AppendFiles)
187 {
188         void main()
189         {
190                 Log.root.add (new AppendFiles ("foo", 5, 6));
191                 auto log = Log.lookup ("fu.bar");
192                 log.trace ("hello {}", "world");
193                 log.trace ("hello {}", "world");
194                 log.trace ("hello {}", "world");
195                 log.trace ("hello {}", "world");
196                 log.trace ("hello {}", "world");
197                 log.trace ("hello {}", "world");
198                 log.trace ("hello {}", "world");
199                 log.trace ("hello {}", "world");
200                 log.trace ("hello {}", "world");
201                 log.trace ("hello {}", "world");
202                 log.trace ("hello {}", "world");
203                 log.trace ("hello {}", "world");
204                 log.trace ("hello {}", "world");
205                 log.trace ("hello {}", "world");
206                 log.trace ("hello {}", "world");
207                 log.trace ("hello {}", "world");
208 
209         }
210 }