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
);
77 if (program_identifier
.Length
> 6)
78 program_identifier_truncated
= program_identifier
.Substring (0, 6);
80 program_identifier_truncated
= program_identifier
;
82 log_writer
= NewLogWriter (program_identifier
);
83 exception_writer
= NewDelayedLogWriter (program_identifier
+ "Exceptions");
85 TextWriter console_log_writer
;
86 console_log_writer
= NewDelayedLogWriter (program_identifier
+ "Console");
88 TextWriter console_redirect_writer
;
89 if (running_in_foreground
) {
90 foreground_echo_writer
= Console
.Out
;
91 console_redirect_writer
= new TeeTextWriter (Console
.Out
, console_log_writer
);
93 console_redirect_writer
= console_log_writer
;
96 Console
.SetOut (console_redirect_writer
);
97 Console
.SetError (console_redirect_writer
);
99 // If we are running in the background, redirect stdin to /dev/null
100 if (! running_in_foreground
) {
101 FileStream dev_null_stream
= new FileStream ("/dev/null",
104 FileShare
.ReadWrite
);
105 TextReader dev_null_reader
= new StreamReader (dev_null_stream
);
106 Console
.SetIn (dev_null_reader
);
110 static public void Disable ()
112 cutoff_level
= LogLevel
.Ignored
;
115 static private void PruneOldLogs ()
117 DateTime magic_date
= DateTime
.Now
.AddDays (-7);
118 DirectoryInfo dir
= new DirectoryInfo (log_directory
);
121 current_str
= "current-" + program_identifier
;
123 foreach (FileInfo file
in dir
.GetFiles ()) {
125 // Clean up old symlinks
126 if (file
.Name
.StartsWith (current_str
) && FileSystem
.IsSymLink (file
.FullName
)) {
127 // Work around a Mono bug in which FileInfo.Delete()
128 // doesn't delete dangling symlinks. See
129 // http://bugzilla.ximian.com/show_bug.cgi?id=78664
132 File
.Delete (file
.FullName
);
137 int last_dash
= file
.Name
.LastIndexOf ("-");
139 continue; // skip strange-looking files
141 string date
= file
.Name
.Substring (0, last_dash
);
145 log_date
= DateTime
.ParseExact (date
, "yyyy-MM-dd-HH-mm-ss", null);
146 if (log_date
< magic_date
)
148 } catch (Exception e
) { }
152 static private TextWriter
NewLogWriter (string name
)
155 log_path
= Path
.Combine (log_directory
, log_name_prefix
+ name
);
158 stream
= new FileStream (log_path
,
161 FileShare
.ReadWrite
);
164 writer
= new StreamWriter (stream
);
165 writer
.AutoFlush
= true;
168 log_link
= Path
.Combine (log_directory
, "current-" + name
);
169 Mono
.Unix
.Native
.Syscall
.symlink (log_path
, log_link
);
174 private class DelayedClosure
{
178 public DelayedClosure (string name
)
183 public TextWriter
Build ()
185 return NewLogWriter (name
);
189 static private TextWriter
NewDelayedLogWriter (string name
)
191 DelayedClosure closure
;
192 closure
= new DelayedClosure (name
);
193 return new DelayedTextWriter (new DelayedTextWriter
.Builder (closure
.Build
));
196 /////////////////////////////////////////////////////////////////////////////////////////
198 static object write_lock
= new object ();
200 static private void WriteLine (LogLevel level
, string format
, object [] args
, Exception ex
)
202 if (cutoff_level
< level
)
205 string ex_str
= null;
207 ex_str
= ex
.ToString ();
209 // This only happens if Log.Initialize was never called.
210 if (running_in_foreground
&& foreground_echo_writer
== null)
211 foreground_echo_writer
= Console
.Out
;
213 if (foreground_echo_writer
!= null) {
214 foreground_echo_writer
.Write (level
);
215 foreground_echo_writer
.Write (": ");
217 foreground_echo_writer
.WriteLine (format
, args
);
219 foreground_echo_writer
.WriteLine (ex_str
);
220 foreground_echo_writer
.Flush ();
223 if (log_writer
== null) // i.e. if Log.Initialize has not been called
226 StringBuilder prefix_builder
;
227 prefix_builder
= new StringBuilder ();
228 prefix_builder
.Append ('\n'); // start w/ a newline
229 prefix_builder
.AppendFormat ("{0:yyMMdd HHmmssffff} {1:00000} ",
230 DateTime
.Now
, Process
.GetCurrentProcess ().Id
);
231 prefix_builder
.Append (program_identifier_truncated
);
233 prefix_builder
.Append (' ');
236 prefix_builder
.Append ("ERROR");
239 prefix_builder
.Append (" WARN");
242 prefix_builder
.Append ("DEBUG");
245 prefix_builder
.Append (" HUH?");
250 prefix_builder
.Append (" EX");
251 prefix_builder
.Append (": ");
254 prefix
= prefix_builder
.ToString ();
256 StringBuilder message
;
257 message
= new StringBuilder ();
258 message
.Append (prefix
);
259 message
.Remove (0, 1); // remove leading \n
261 message
.AppendFormat (format
, args
);
262 if (ex_str
!= null) {
264 message
.Append ('\n');
265 message
.Append (ex_str
);
267 message
.Replace ("\n", prefix
);
270 message_str
= message
.ToString ();
273 log_writer
.WriteLine (message_str
);
274 if (ex
!= null && exception_writer
!= null)
275 exception_writer
.WriteLine (message_str
);
279 /////////////////////////////////////////////////////////////////////////////////////////
281 static public void Debug (string message
, params object [] args
)
283 WriteLine (LogLevel
.Debug
, message
, args
, null);
286 static public void Debug (Exception ex
, string message
, params object [] args
)
288 WriteLine (LogLevel
.Debug
, message
, args
, ex
);
291 static public void Debug (Exception ex
)
293 WriteLine (LogLevel
.Debug
, null, null, ex
);
296 static public void Info (string message
, params object [] args
)
298 // The Info log level is deprecated: just map it to Debug.
299 Debug (message
, args
);
302 static public void Info (Exception ex
, string message
, params object [] args
)
304 Debug (ex
, message
, args
);
307 static public void Info (Exception ex
)
312 static public void Warn (string message
, params object [] args
)
314 WriteLine (LogLevel
.Warn
, message
, args
, null);
317 static public void Warn (Exception ex
, string message
, params object [] args
)
319 WriteLine (LogLevel
.Warn
, message
, args
, ex
);
322 static public void Warn (Exception ex
)
324 WriteLine (LogLevel
.Warn
, null, null, ex
);
327 static public void Error (string message
, params object [] args
)
329 WriteLine (LogLevel
.Error
, message
, args
, null);
332 static public void Error (Exception ex
, string message
, params object [] args
)
334 WriteLine (LogLevel
.Error
, message
, args
, ex
);
337 static public void Error (Exception ex
)
339 WriteLine (LogLevel
.Error
, null, null, ex
);