4 // Copyright (C) 2004-2005 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 using System
.Collections
;
31 using System
.Diagnostics
;
33 namespace Beagle
.Util
{
35 public enum LogLevel
{
42 static public class Log
{
44 static private string log_directory
;
45 static private string log_name_prefix
;
46 static private string program_identifier
;
47 static private string program_identifier_truncated
;
49 // If we don't call Log.Initialize, these defaults ensure that
50 // everything will just get spewed to the console.
51 static private bool running_in_foreground
= true;
52 static private LogLevel cutoff_level
= LogLevel
.Debug
;
54 static private TextWriter log_writer
;
55 static private TextWriter exception_writer
;
56 static private TextWriter foreground_echo_writer
;
58 static public LogLevel Level
{
59 get { return cutoff_level; }
60 set { cutoff_level = value; }
63 static public void Initialize (string log_directory
,
64 string program_identifier
,
65 LogLevel cutoff_level
,
66 bool running_in_foreground
)
68 Log
.log_directory
= log_directory
;
69 Log
.program_identifier
= program_identifier
;
70 Log
.cutoff_level
= cutoff_level
;
71 Log
.running_in_foreground
= running_in_foreground
;
75 log_name_prefix
= String
.Format ("{0:yyyy-MM-dd-HH-mm-ss}-", DateTime
.Now
);
76 program_identifier_truncated
= program_identifier
.Substring (0, 6);
78 log_writer
= NewLogWriter (program_identifier
);
79 exception_writer
= NewDelayedLogWriter (program_identifier
+ "Exceptions");
81 TextWriter console_log_writer
;
82 console_log_writer
= NewDelayedLogWriter (program_identifier
+ "Console");
84 TextWriter console_redirect_writer
;
85 if (running_in_foreground
) {
86 foreground_echo_writer
= Console
.Out
;
87 console_redirect_writer
= new TeeTextWriter (Console
.Out
, console_log_writer
);
89 console_redirect_writer
= console_log_writer
;
92 Console
.SetOut (console_redirect_writer
);
93 Console
.SetError (console_redirect_writer
);
95 // If we are running in the background, redirect stdin to /dev/null
96 if (! running_in_foreground
) {
97 FileStream dev_null_stream
= new FileStream ("/dev/null",
100 FileShare
.ReadWrite
);
101 TextReader dev_null_reader
= new StreamReader (dev_null_stream
);
102 Console
.SetIn (dev_null_reader
);
106 static public void Disable ()
108 cutoff_level
= LogLevel
.Ignored
;
111 static private void PruneOldLogs ()
113 DateTime magic_date
= DateTime
.Now
.AddDays (-7);
114 DirectoryInfo dir
= new DirectoryInfo (log_directory
);
117 current_str
= "current-" + program_identifier
;
119 foreach (FileInfo file
in dir
.GetFiles ()) {
121 // Clean up old symlinks
122 if (file
.Name
.StartsWith (current_str
) && FileSystem
.IsSymLink (file
.FullName
)) {
123 // Work around a Mono bug in which FileInfo.Delete()
124 // doesn't delete dangling symlinks. See
125 // http://bugzilla.ximian.com/show_bug.cgi?id=78664
128 File
.Delete (file
.FullName
);
133 int last_dash
= file
.Name
.LastIndexOf ("-");
135 continue; // skip strange-looking files
137 string date
= file
.Name
.Substring (0, last_dash
);
141 log_date
= DateTime
.ParseExact (date
, "yyyy-MM-dd-HH-mm-ss", null);
142 if (log_date
< magic_date
)
144 } catch (Exception e
) { }
148 static private TextWriter
NewLogWriter (string name
)
151 log_path
= Path
.Combine (log_directory
, log_name_prefix
+ name
);
154 stream
= new FileStream (log_path
,
157 FileShare
.ReadWrite
);
160 writer
= new StreamWriter (stream
);
161 writer
.AutoFlush
= true;
164 log_link
= Path
.Combine (log_directory
, "current-" + name
);
165 Mono
.Unix
.Native
.Syscall
.symlink (log_path
, log_link
);
170 private class DelayedClosure
{
174 public DelayedClosure (string name
)
179 public TextWriter
Build ()
181 return NewLogWriter (name
);
185 static private TextWriter
NewDelayedLogWriter (string name
)
187 DelayedClosure closure
;
188 closure
= new DelayedClosure (name
);
189 return new DelayedTextWriter (new DelayedTextWriter
.Builder (closure
.Build
));
192 /////////////////////////////////////////////////////////////////////////////////////////
194 static object write_lock
= new object ();
196 static private void WriteLine (LogLevel level
, string format
, object [] args
, Exception ex
)
198 if (cutoff_level
< level
)
201 string ex_str
= null;
203 ex_str
= ex
.ToString ();
205 // This only happens if Log.Initialize was never called.
206 if (running_in_foreground
&& foreground_echo_writer
== null)
207 foreground_echo_writer
= Console
.Out
;
209 if (foreground_echo_writer
!= null) {
210 foreground_echo_writer
.Write (level
);
211 foreground_echo_writer
.Write (": ");
213 foreground_echo_writer
.WriteLine (format
, args
);
215 foreground_echo_writer
.WriteLine (ex_str
);
216 foreground_echo_writer
.Flush ();
219 if (log_writer
== null) // i.e. if Log.Initialize has not been called
222 StringBuilder prefix_builder
;
223 prefix_builder
= new StringBuilder ();
224 prefix_builder
.Append ('\n'); // start w/ a newline
225 prefix_builder
.AppendFormat ("{0:yyMMdd HHmmssffff} {1:00000} ",
226 DateTime
.Now
, Process
.GetCurrentProcess ().Id
);
227 prefix_builder
.Append (program_identifier_truncated
);
229 prefix_builder
.Append (' ');
232 prefix_builder
.Append ("ERROR");
235 prefix_builder
.Append (" WARN");
238 prefix_builder
.Append ("DEBUG");
241 prefix_builder
.Append (" HUH?");
246 prefix_builder
.Append (" EX");
247 prefix_builder
.Append (": ");
250 prefix
= prefix_builder
.ToString ();
252 StringBuilder message
;
253 message
= new StringBuilder ();
254 message
.Append (prefix
);
255 message
.Remove (0, 1); // remove leading \n
257 message
.AppendFormat (format
, args
);
258 if (ex_str
!= null) {
260 message
.Append ('\n');
261 message
.Append (ex_str
);
263 message
.Replace ("\n", prefix
);
266 message_str
= message
.ToString ();
269 log_writer
.WriteLine (message_str
);
270 if (ex
!= null && exception_writer
!= null)
271 exception_writer
.WriteLine (message_str
);
275 /////////////////////////////////////////////////////////////////////////////////////////
277 static public void Debug (string message
, params object [] args
)
279 WriteLine (LogLevel
.Debug
, message
, args
, null);
282 static public void Debug (Exception ex
, string message
, params object [] args
)
284 WriteLine (LogLevel
.Debug
, message
, args
, ex
);
287 static public void Debug (Exception ex
)
289 WriteLine (LogLevel
.Debug
, null, null, ex
);
292 static public void Info (string message
, params object [] args
)
294 // The Info log level is deprecated: just map it to Debug.
295 Debug (message
, args
);
298 static public void Info (Exception ex
, string message
, params object [] args
)
300 Debug (ex
, message
, args
);
303 static public void Info (Exception ex
)
308 static public void Warn (string message
, params object [] args
)
310 WriteLine (LogLevel
.Warn
, message
, args
, null);
313 static public void Warn (Exception ex
, string message
, params object [] args
)
315 WriteLine (LogLevel
.Warn
, message
, args
, ex
);
318 static public void Warn (Exception ex
)
320 WriteLine (LogLevel
.Warn
, null, null, ex
);
323 static public void Error (string message
, params object [] args
)
325 WriteLine (LogLevel
.Error
, message
, args
, null);
328 static public void Error (Exception ex
, string message
, params object [] args
)
330 WriteLine (LogLevel
.Error
, message
, args
, ex
);
333 static public void Error (Exception ex
)
335 WriteLine (LogLevel
.Error
, null, null, ex
);