FSharp.Logf


FSharp.Logf

This library implements printf-style logging functions for any Microsoft.Extensions.Logging.ILogger, allowing you to log in an F# style with the full power of structured logging. Here's an example, adding logging to the snippet from https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/async#combine-asynchronous-computations:

// Put this in a project and reference these packages: FSharp.Logf, Microsoft.Extensions.Logging, Microsoft.Extensions.Logging.Console
open System
open System.IO
open Microsoft.Extensions.Logging
open FSharp.Logf

// Type annotation would be inferred if omitted (included here for clarity)
let printTotalFileBytes (ml: ILogger) path =
    async {
        try
            let! bytes = File.ReadAllBytesAsync(path) |> Async.AwaitTask
            let fileName = Path.GetFileName(path)
            // Log at information level, with fileName and bytesLength as the parameter names for any logging sinks
            // supporting structured logging
            logfi ml "File %s{fileName} has %d{bytesLength} bytes" fileName bytes.Length
        with e -> 
            // Log at error level, setting an exception
            elogfe ml e "Exception accessing file: '%s{path}'" path
    }

[<EntryPoint>]
let main argv =
    // Create a Microsoft-provided logger. Choose your favorite Logger provider (for example: Serilog, NLog, log4net)
    let logger = LoggerFactory.Create(fun builder -> builder.AddConsole().SetMinimumLevel(LogLevel.Debug) |> ignore).CreateLogger()
    
    // Log at debug level. Since the NewLine argument doesn't have a parameter name right after it, it will be baked
    // directly into the string. The argv argument, however, will be parameterized like the others.
    logfd logger "ARGV:%s%s{argv}" Environment.NewLine ("[|" + (argv |> String.concat ";") + "|]")

    argv
    |> Seq.map (printTotalFileBytes logger)
    |> Async.Parallel
    |> Async.Ignore
    |> Async.RunSynchronously
    
    0

Fable compatability

This library is Fable-compatible. You can take advantage of this like so:

#if !FABLE_COMPILER
open Microsoft.Extensions.Logging
open FSharp.Logf
#else
open Fable.Microsoft.Extensions.Logging
open Fable.FSharp.Logf
#endif

let ml =
#if !FABLE_COMPILER
    LoggerFactory.Create(fun builder -> builder.AddConsole().SetMinimumLevel(LogLevel.Debug) |> ignore)
#else
    ConsoleLogger()
#endif

logfi ml "Hello, %s{arg}!" "world"
namespace System
namespace System.IO
namespace Microsoft
namespace Microsoft.FSharp
val printTotalFileBytes : ml:'a -> path:string -> Async<unit>
val ml : 'a
val path : string
val async : AsyncBuilder
<summary>Builds an asynchronous workflow using computation expression syntax.</summary>
val bytes : byte []
type File = static member AppendAllLines : path: string * contents: IEnumerable<string> -> unit + 1 overload static member AppendAllLinesAsync : path: string * contents: IEnumerable<string> * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload static member AppendAllText : path: string * contents: string -> unit + 1 overload static member AppendAllTextAsync : path: string * contents: string * encoding: Encoding *?cancellationToken: CancellationToken -> Task + 1 overload static member AppendText : path: string -> StreamWriter static member Copy : sourceFileName: string * destFileName: string -> unit + 1 overload static member Create : path: string -> FileStream + 2 overloads static member CreateSymbolicLink : path: string * pathToTarget: string -> FileSystemInfo static member CreateText : path: string -> StreamWriter static member Decrypt : path: string -> unit ...
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
File.ReadAllBytesAsync(path: string,?cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task<byte []>
Multiple items
type Async = static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit) static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate) static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool> static member AwaitTask : task:Task<'T> -> Async<'T> + 1 overload static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool> static member CancelDefaultToken : unit -> unit static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>> static member Choice : computations:seq<Async<'T option>> -> Async<'T option> static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...
<summary>Holds static members for creating and manipulating asynchronous computations.</summary>
<remarks> See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>. </remarks>
<category index="1">Async Programming</category>


--------------------
type Async<'T> =
<summary> An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception. </summary>
<remarks> This type has no members. Asynchronous computations are normally specified either by using an async expression or the static methods in the <see cref="T:Microsoft.FSharp.Control.Async" /> type. See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">F# Language Guide - Async Workflows</a>. </remarks>
<namespacedoc><summary> Library functionality for asynchronous programming, events and agents. See also <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/asynchronous-workflows">Asynchronous Programming</a>, <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/members/events">Events</a> and <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/lazy-expressions">Lazy Expressions</a> in the F# Language Guide. </summary></namespacedoc>
<category index="1">Async Programming</category>
static member Async.AwaitTask : task:Threading.Tasks.Task -> Async<unit>
static member Async.AwaitTask : task:Threading.Tasks.Task<'T> -> Async<'T>
val fileName : string
type Path = static member ChangeExtension : path: string * extension: string -> string static member Combine : path1: string * path2: string -> string + 3 overloads static member EndsInDirectorySeparator : path: ReadOnlySpan<char> -> bool + 1 overload static member GetDirectoryName : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetExtension : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFileName : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFileNameWithoutExtension : path: ReadOnlySpan<char> -> ReadOnlySpan<char> + 1 overload static member GetFullPath : path: string -> string + 1 overload static member GetInvalidFileNameChars : unit -> char [] static member GetInvalidPathChars : unit -> char [] ...
<summary>Performs operations on <see cref="T:System.String" /> instances that contain file or directory path information. These operations are performed in a cross-platform manner.</summary>
Path.GetFileName(path: string) : string
Path.GetFileName(path: ReadOnlySpan<char>) : ReadOnlySpan<char>
property Array.Length: int with get
<summary>Gets the total number of elements in all the dimensions of the <see cref="T:System.Array" />.</summary>
<exception cref="T:System.OverflowException">The array is multidimensional and contains more than <see cref="F:System.Int32.MaxValue" /> elements.</exception>
<returns>The total number of elements in all the dimensions of the <see cref="T:System.Array" />; zero if there are no elements in the array.</returns>
val e : exn
Multiple items
type EntryPointAttribute = inherit Attribute new : unit -> EntryPointAttribute
<summary>Adding this attribute to a function indicates it is the entrypoint for an application. If this attribute is not specified for an EXE then the initialization implicit in the module bindings in the last file in the compilation sequence are used as the entrypoint.</summary>
<category>Attributes</category>


--------------------
new : unit -> EntryPointAttribute
val main : argv:string [] -> int
val argv : string []
val logger : obj
val ignore : value:'T -> unit
<summary>Ignore the passed value. This is often used to throw away results of a computation.</summary>
<param name="value">The value to ignore.</param>
type Environment = static member Exit : exitCode: int -> unit static member ExpandEnvironmentVariables : name: string -> string static member FailFast : message: string -> unit + 1 overload static member GetCommandLineArgs : unit -> string [] static member GetEnvironmentVariable : variable: string -> string + 1 overload static member GetEnvironmentVariables : unit -> IDictionary + 1 overload static member GetFolderPath : folder: SpecialFolder -> string + 1 overload static member GetLogicalDrives : unit -> string [] static member SetEnvironmentVariable : variable: string * value: string -> unit + 1 overload static member CommandLine : string ...
<summary>Provides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.</summary>
property Environment.NewLine: string with get
<summary>Gets the newline string defined for this environment.</summary>
<returns><see langword="\r\n" /> for non-Unix platforms, or <see langword="\n" /> for Unix platforms.</returns>
Multiple items
type String = interface IEnumerable<char> interface IEnumerable interface ICloneable interface IComparable interface IComparable<string> interface IConvertible interface IEquatable<string> new : value: nativeptr<char> -> unit + 8 overloads member Clone : unit -> obj member CompareTo : value: obj -> int + 1 overload ...
<summary>Represents text as a sequence of UTF-16 code units.</summary>

--------------------
String(value: nativeptr<char>) : String
String(value: char []) : String
String(value: ReadOnlySpan<char>) : String
String(value: nativeptr<sbyte>) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
val concat : sep:string -> strings:seq<string> -> string
<summary>Returns a new string made by concatenating the given strings with separator <c>sep</c>, that is <c>a1 + sep + ... + sep + aN</c>.</summary>
<param name="sep">The separator string to be inserted between the strings of the input sequence.</param>
<param name="strings">The sequence of strings to be concatenated.</param>
<returns>A new string consisting of the concatenated strings separated by the separation string.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when <c>strings</c> is null.</exception>
module Seq from Microsoft.FSharp.Collections
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.seq`1" />.</summary>
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>
<summary>Builds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The given function will be applied as elements are demanded using the <c>MoveNext</c> method on enumerators retrieved from the object.</summary>
<remarks>The returned sequence may be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should not be accessed concurrently.</remarks>
<param name="mapping">A function to transform items from the input sequence.</param>
<param name="source">The input sequence.</param>
<returns>The result sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
static member Async.Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member Async.Parallel : computations:seq<Async<'T>> * ?maxDegreeOfParallelism:int -> Async<'T []>
static member Async.Ignore : computation:Async<'T> -> Async<unit>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:Threading.CancellationToken -> 'T
val ml : obj