- let maxSizeFile = 10L * 1024L * 1024L // [byte] (10 MB).
- let nbEntriesCheckSize = 100; // Each 100 entries added we check the size of the log file to test if it is greater than 'MAX_SIZE_FILE'.
- let LogDefaultDirectory = "Parasitemia\\Log"
- let filenameFormat = "{0:D4}.log"
- let encoding = Encoding.GetEncoding("UTF-8")
-
- let moduleName = System.Diagnostics.StackFrame(1).GetMethod().Module.Name
-
- let mutable stream: StreamWriter = null
-
- let mutable logDir: string = null
- let mutable absoluteDir: string = null
-
- let mutable nbEntries = 0L
-
- let monitor = Object()
-
- let listeners = List<IListener>()
-
- let debug =
-#if DEBUG
- true
-#else
- false
-#endif
-
- static let instance = new Log()
-
- let setLogDirectory (dir: string) =
- lock monitor (fun () ->
- logDir <- dir
- absoluteDir <- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir)
-
- if stream <> null
- then
- stream.Close()
- stream <- null
-
- try
- if not <| Directory.Exists(absoluteDir)
- then
- Directory.CreateDirectory(absoluteDir) |> ignore
- with
- | _ as ex -> Console.Error.WriteLine("Unable to create the log directory: {0}", absoluteDir))
-
- let openLogFile () =
- try
- if stream = null || (nbEntries % (int64 nbEntriesCheckSize) = 0L) && stream.BaseStream.Length > maxSizeFile
- then
- if stream <> null
- then
- stream.Close()
-
- let mutable n = 1
- for existingFile in Directory.GetFiles(absoluteDir) do
- let current_n = ref 0
- if Int32.TryParse(existingFile.Remove(existingFile.LastIndexOf('.')), current_n) && !current_n > n
- then
- n <- !current_n
-
- let mutable filename = Path.Combine(absoluteDir, String.Format(filenameFormat, n))
- try
- if (FileInfo(filename).Length > maxSizeFile)
- then
- filename <- Path.Combine(absoluteDir, String.Format(filenameFormat, n + 1))
- with
- | :? FileNotFoundException -> () // The file may not exist.
-
- stream <- new StreamWriter(filename, true, encoding)
- with
- | _ as ex -> Console.Error.WriteLine("Can't open the file log: {0}", ex)
-
- do
- setLogDirectory LogDefaultDirectory
-
- interface IDisposable with
- member this.Dispose () =
- if stream <> null
- then
- stream.Dispose()
-
- member private this.Write (message: string, severity: Severity) =
- lock monitor (fun () ->
- nbEntries <- nbEntries + 1L
- openLogFile ()
-
- if stream <> null
- then
- let mutable moduleNameCaller = moduleName
- match StackTrace().GetFrames() |> Array.tryPick (fun frame -> let name = frame.GetMethod().Module.Name
- if name <> moduleName then Some name else None) with
- | Some name -> moduleNameCaller <- name
- | _ -> ()
-
- let threadName = Thread.CurrentThread.Name
-
- for listener in listeners do
- listener.NewEntry severity message
-
- try
- stream.WriteLine(
- "{0:yyyy-MM-dd HH:mm:ss.fff} [{1}] {{{2}}} ({3}) : {4}",
- TimeZone.CurrentTimeZone.ToLocalTime(DateTime.UtcNow),
- severity.ToString(),
- moduleNameCaller,
- (if String.IsNullOrEmpty(threadName) then Thread.CurrentThread.ManagedThreadId.ToString() else String.Format("{0}-{1}", threadName, Thread.CurrentThread.ManagedThreadId)),
- message
- )
- stream.Flush()
- with
- | :? IOException as ex -> Console.Error.WriteLine("Unable to write to the log file: {0}", ex))
-
-
- member private this.AddListener (listener: IListener) =
- lock monitor (fun () ->
- if not <| listeners.Contains(listener)
- then
- listeners.Add(listener))
-
- member private this.RmListener (listener: IListener) =
- lock monitor (fun () ->
- listeners.Remove(listener) |> ignore)
-
- static member AddListener (listener: IListener) = instance.AddListener(listener)
- static member RmListener (listener: IListener) = instance.RmListener(listener)
-
- static member LogWithTime (message: string, severity: Severity, f: unit -> 'a option, [<ParamArray>] args: Object[]) : 'a option =
- let sw = Stopwatch()
- sw.Start()
+ static let mutable writer : IWriter = new ConsoleWriter () :> IWriter
+ static let monitor = obj ()
+ static let listeners = Listeners ()
+
+ /// <summary>
+ /// Must be called first before any other action.
+ /// </summary>
+ static member LogDirectory
+ with get () = lock monitor (fun () -> writer.LogDir)
+ and set value =
+ lock monitor (
+ fun () ->
+ Log.Close ()
+ if String.IsNullOrWhiteSpace value then
+ writer <- new ConsoleWriter ()
+ else
+ writer <- new FileWriter (value)
+ )
+
+ /// <summary>
+ /// Close the log. 'LogDirectory' must be set again to reopen it.
+ /// </summary>
+ static member Close () =
+ lock monitor (
+ fun () ->
+ writer.Flush ()
+ (writer :> IDisposable).Dispose ()
+ writer <- new ConsoleWriter () :> IWriter
+ )
+
+ /// <summary>
+ /// Return all log files (the current one and the archived) as full paths.
+ /// </summary>
+ static member LogFiles = writer.LogFiles
+
+ /// <summary>
+ /// Wait that all the previous messages are written.
+ /// </summary>
+ static member Flush () = writer.Flush ()
+
+ static member DebugLoggingEnabled
+ with get () = writer.DebugLoggingEnabled
+ and set value = writer.DebugLoggingEnabled <- value
+
+ /// <summary>
+ /// Avoid to repeat a message by writting a reference to a previous message instead of the message.
+ /// 'false' by default.
+ /// </summary>
+ static member AvoidRepeatingIdenticalMessages
+ with get () = writer.AvoidRepeatingIdenticalMessages
+ and set value = writer.AvoidRepeatingIdenticalMessages <- value
+
+ /// <summary>
+ /// The maximum size of the current file log. If the file exceed this value it will be zipped and a new file will be created.
+ /// The file size is only tested each time a certain number of messages have been written so the file may exceed this value a bit.
+ /// </summary>
+ /// <param name="size"></param>
+ static member SetLogFilesMaxSize (size : int64) =
+ writer.MaxSizeFile <- size
+
+ static member ClearLogFilesOlderThan (timeOld : TimeSpan) =
+ writer.ClearLogFilesOlderThan timeOld
+
+ /// <summary>
+ /// Remove all archived log files and empty the current one.
+ /// </summary>
+ static member ClearLogFiles () =
+ Log.ClearLogFilesOlderThan (TimeSpan 0L)
+
+ /// <summary>
+ /// Total size in bytes.
+ /// </summary>
+ static member CurrentLogSize () : int64 =
+ Log.LogFiles
+ |> Seq.map (fun file -> try (FileInfo file).Length with | _ex -> 0L)
+ |> Seq.sum
+
+ static member AddListener (listener : IListener) =
+ listeners.Add listener
+
+ static member RemoveListener (listener : IListener) =
+ listeners.Remove listener
+
+ static member private Write (message : string) (severity : Severity) =
+ let msg =
+ {
+ Message = message
+ ThreadName = Thread.CurrentThread.Name
+ ThreadId = Thread.CurrentThread.ManagedThreadId
+ ModuleCaller = Utils.callerModuleName ()
+ Severity = severity
+ DateTime = TimeZone.CurrentTimeZone.ToLocalTime DateTime.UtcNow
+ }
+ listeners.NewEntry msg
+ writer.Write msg
+
+ /// <summary>
+ /// [F#] Execute the given function and measure its time.
+ /// </summary>
+ /// <param name="severity">Severity for writing to log</param>
+ /// <param name="f">Function to test</param>
+ /// <param name="format">Format string for output</param>
+ static member LogWithTime (severity : Severity) (f : unit -> 'a) (format : Printf.StringFormat<'b, 'a>) : 'b =
+ let sw = Stopwatch ()
+ sw.Start ()