132
|
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.RollingFileAppender;
|
|
14
|
|
15 private import tango.time.Time;
|
|
16
|
|
17 private import tango.io.FilePath,
|
|
18 tango.io.FileConst,
|
|
19 tango.io.FileConduit;
|
|
20
|
|
21 private import tango.io.model.IBuffer;
|
|
22
|
|
23 private import tango.util.log.Appender,
|
|
24 tango.util.log.FileAppender;
|
|
25
|
|
26 /*******************************************************************************
|
|
27
|
|
28 Append log messages to a file set.
|
|
29
|
|
30 *******************************************************************************/
|
|
31
|
|
32 public class RollingFileAppender : FileAppender
|
|
33 {
|
|
34 private Mask mask;
|
|
35 private FilePath[] paths;
|
|
36 private int index;
|
|
37 private IBuffer buffer;
|
|
38 private ulong maxSize,
|
|
39 fileSize;
|
|
40
|
|
41 /***********************************************************************
|
|
42
|
|
43 Create a RollingFileAppender upon a file-set with the
|
|
44 specified path and optional layout.
|
|
45
|
|
46 Where a file set already exists, we resume appending to
|
|
47 the one with the most recent activity timestamp.
|
|
48
|
|
49 ***********************************************************************/
|
|
50
|
|
51 this (char[] path, int count, ulong maxSize, EventLayout layout = null)
|
|
52 {
|
|
53 assert (path);
|
|
54 assert (count > 1 && count < 10);
|
|
55
|
|
56 // Get a unique fingerprint for this instance
|
|
57 mask = register (path);
|
|
58
|
|
59 char[1] x;
|
|
60 Time mostRecent;
|
|
61
|
|
62 for (int i=0; i < count; ++i)
|
|
63 {
|
|
64 x[0] = '0' + i;
|
|
65
|
|
66 auto p = new FilePath (path);
|
|
67 p.name = p.name ~ x;
|
|
68 paths ~= p;
|
|
69
|
|
70 // use the most recent file in the set
|
|
71 if (p.exists)
|
|
72 {
|
|
73 auto modified = p.modified;
|
|
74 if (modified > mostRecent)
|
|
75 {
|
|
76 mostRecent = modified;
|
|
77 index = i;
|
|
78 }
|
|
79 }
|
|
80 }
|
|
81
|
|
82 // remember the maximum size
|
|
83 this.maxSize = maxSize;
|
|
84
|
|
85 // adjust index and open the appropriate log file
|
|
86 --index;
|
|
87 nextFile (false);
|
|
88
|
|
89 // set provided layout (ignored when null)
|
|
90 setLayout (layout);
|
|
91 }
|
|
92
|
|
93 /***********************************************************************
|
|
94
|
|
95 Return the fingerprint for this class
|
|
96
|
|
97 ***********************************************************************/
|
|
98
|
|
99 Mask getMask ()
|
|
100 {
|
|
101 return mask;
|
|
102 }
|
|
103
|
|
104 /***********************************************************************
|
|
105
|
|
106 Return the name of this class
|
|
107
|
|
108 ***********************************************************************/
|
|
109
|
|
110 char[] getName ()
|
|
111 {
|
|
112 return this.classinfo.name;
|
|
113 }
|
|
114
|
|
115 /***********************************************************************
|
|
116
|
|
117 Append an event to the output.
|
|
118
|
|
119 ***********************************************************************/
|
|
120
|
|
121 synchronized void append (Event event)
|
|
122 {
|
|
123 char[] msg;
|
|
124
|
|
125 // file already full?
|
|
126 if (fileSize >= maxSize)
|
|
127 nextFile (true);
|
|
128
|
|
129 // bump file size
|
|
130 fileSize += FileConst.NewlineString.length;
|
|
131
|
|
132 // write log message and flush it
|
|
133 auto layout = getLayout ();
|
|
134 msg = layout.header (event);
|
|
135 fileSize += msg.length;
|
|
136 buffer.append (msg);
|
|
137
|
|
138 msg = layout.content (event);
|
|
139 fileSize += msg.length;
|
|
140 buffer.append (msg);
|
|
141
|
|
142 msg = layout.footer (event);
|
|
143 fileSize += msg.length;
|
|
144 buffer.append (msg);
|
|
145
|
|
146 buffer.append(FileConst.NewlineString).flush;
|
|
147 }
|
|
148
|
|
149 /***********************************************************************
|
|
150
|
|
151 Switch to the next file within the set
|
|
152
|
|
153 ***********************************************************************/
|
|
154
|
|
155 private void nextFile (bool reset)
|
|
156 {
|
|
157 // select next file in the set
|
|
158 if (++index >= paths.length)
|
|
159 index = 0;
|
|
160
|
|
161 // reset file size
|
|
162 fileSize = 0;
|
|
163
|
|
164 // close any existing conduit
|
|
165 close;
|
|
166
|
|
167 // make it shareable for read
|
|
168 auto style = FileConduit.WriteAppending;
|
|
169 style.share = FileConduit.Share.Read;
|
|
170 auto conduit = new FileConduit (paths[index], style);
|
|
171
|
|
172 buffer = setConduit (conduit);
|
|
173 if (reset)
|
|
174 conduit.truncate;
|
|
175 }
|
|
176 }
|
|
177
|