namespace Logger open System open System.Diagnostics open System.IO open System.Threading open Logger open Logger.Types [] type Log () = static let mutable writer : IWriter = new ConsoleWriter () :> IWriter static let monitor = obj () static let listeners = Listeners () /// /// Must be called first before any other action. /// 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) ) /// /// Close the log. 'LogDirectory' must be set again to reopen it. /// static member Close () = lock monitor ( fun () -> writer.Flush () (writer :> IDisposable).Dispose () writer <- new ConsoleWriter () :> IWriter ) /// /// Return all log files (the current one and the archived) as full paths. /// static member LogFiles = writer.LogFiles /// /// Wait that all the previous messages are written. /// static member Flush () = writer.Flush () static member DebugLoggingEnabled with get () = writer.DebugLoggingEnabled and set value = writer.DebugLoggingEnabled <- value /// /// Avoid to repeat a message by writting a reference to a previous message instead of the message. /// 'false' by default. /// static member AvoidRepeatingIdenticalMessages with get () = writer.AvoidRepeatingIdenticalMessages and set value = writer.AvoidRepeatingIdenticalMessages <- value /// /// 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. /// /// static member SetLogFilesMaxSize (size : int64) = writer.MaxSizeFile <- size static member ClearLogFilesOlderThan (timeOld : TimeSpan) = writer.ClearLogFilesOlderThan timeOld /// /// Remove all archived log files and empty the current one. /// static member ClearLogFiles () = Log.ClearLogFilesOlderThan (TimeSpan 0L) /// /// Total size in bytes. /// 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 /// /// [F#] Execute the given function and measure its time. /// /// Severity for writing to log /// Function to test /// Format string for output static member LogWithTime (severity : Severity) (f : unit -> 'a) (format : Printf.StringFormat<'b, 'a>) : 'b = let sw = Stopwatch () sw.Start () let res = f () sw.Stop () Printf.kprintf (fun s -> Log.Write (s + sprintf " (time: %d ms)" sw.ElapsedMilliseconds) severity; res) format /// /// [F#] Write Debug message to log (if DebugLoggingEnabled = true) /// static member Debug format = if writer.DebugLoggingEnabled then Printf.kprintf (fun s -> Log.Write s Severity.DEBUG) format else // [BGR] FIXME: is it possible to simplify a bit here? It's more CPU consuming than the C# couterpart. Printf.kprintf (fun _ -> ()) format /// /// [F#] Write Info message to log /// static member Info format = Printf.kprintf (fun s -> Log.Write s Severity.INFO) format /// /// [F#] Write Warning message to log /// static member Warning format = Printf.kprintf (fun s -> Log.Write s Severity.WARNING) format /// /// [F#] Write Error message to log /// static member Error format = Printf.kprintf (fun s -> Log.Write s Severity.ERROR) format /// /// [F#] Write Fatal message to log /// static member Fatal format = Printf.kprintf (fun s -> Log.Write s Severity.FATAL) format /// /// Write DEBUG message to log (if DebugLoggingEnabled = true) /// static member DEBUG (message : string, [] args : obj array) = if writer.DebugLoggingEnabled then if isNull args || args.Length = 0 then Log.Write message Severity.DEBUG else Log.Write (String.Format (message, args)) Severity.DEBUG /// /// Write DEBUG message to log (if DebugLoggingEnabled = true) /// static member DEBUG (message : string) = Log.DEBUG (message, [| |]) /// /// Write INFO message to log /// static member INFO (message : string, [] args : obj array) = if isNull args || args.Length = 0 then Log.Write message Severity.INFO else Log.Write (String.Format (message, args)) Severity.INFO /// /// Write INFO message to log /// static member INFO (message : string) = Log.INFO (message, [| |]) /// /// Write WARNING message to log /// static member WARNING (message : string, [] args : obj array) = if isNull args || args.Length = 0 then Log.Write message Severity.WARNING else Log.Write (String.Format (message, args)) Severity.WARNING /// /// Write WARNING message to log /// static member WARNING (message : string) = Log.WARNING (message, [| |]) /// /// Write ERROR message to log /// static member ERROR (message : string, [] args : obj array) = if isNull args || args.Length = 0 then Log.Write message Severity.ERROR else Log.Write (String.Format (message, args)) Severity.ERROR /// /// Write ERROR message to log /// static member ERROR (message : string) = Log.ERROR (message, [| |]) /// /// Write FATAL message to log /// static member FATAL (message : string, [] args : obj array) = if isNull args || args.Length = 0 then Log.Write message Severity.FATAL else Log.Write (String.Format (message, args)) Severity.FATAL /// /// Write FATAL message to log /// static member FATAL (message : string) = Log.FATAL (message, [| |])