Using NLog with F # Interactive in Visual Studio - Documentation Required

I need to capture the input and output of F # functions while using F # Interactive. I can get NLog to work fine when the program is launched in Visual Studio using F5 or Ctrl-F5. Also, the same methods that contain instructions for logging are simple and are called when called through F # Interactive; just nothing in the log file.

I also tried the following: F # Interactive for setting NLog links and still nothing in the log when launched from F # Interactive.

#I @"..\packages\NLog.2.0.0.2000\lib\net40" 
#r @"NLog.dll"

      

And I even found this one that made me try each of these

NLog.Config.SimpleConfigurator.ConfigureForConsoleLogging()
NLog.Config.SimpleConfigurator.ConfigureForFileLogging(<full file name>)

      

and still nothing in the log file.

Does anyone know if Nlog can be used with F # Interactive?
If so, how is it done?

EDIT

I was able to get NLog to work with fsi.exe when it runs as a standalone. So now the problem is that NLog is finding the config file as NLog cannot find the config file starting from the fsi.exe location for Visual Studio. Looking at the use of NLog.dll.nlog in the NLog.dll directory.

+3


source to share


1 answer


Problem

The problem with using NLog from F # Interactive is that NLog thinks the directory Temp

is where it can be found NLog.config

and will never succeed. To do this, you need to programmatically find NLog.config

for NLog.

What you need to know to solve this problem:

  • When you start F # Interactive from Visual Studio, it sets the current working directory to a temp file.

    > System.Environment.CurrentDirectory;; 
    val it : string = "C:\Users\Eric\AppData\Local\Temp"
    
          

  • NLog requires three components:
    a. reference to NLog.dll.
    b. The configuration file.
    from. calls the logger method from the code.

  • NLog can be configured in many ways, both programmatically and using configuration files.
  • AppData is a hidden folder. Guess what that means when using Windows Explorer.
  • To get the location of an application with F # Interactive in Visual Studio, you need __SOURCE_DIRECTORY__

    . See F # Spec 3.11 Identifier Substitutions
  • NLog.conf can use the full path to the file. Obvious but necessary.
  • NLog file targets have an autoFlush option.
  • NLog can be installed into a Visual Studio project using NuGet .
  • Most of the information here comes from the NLog Wiki .

Instead of jumping straight into the F # interactive solution, the following progression will be used, as a DLL will need to be created to set up and store functions for use with NLog from F # Interactive.

  • Build a solution with three projects and install NLog.
    Solution Name: NLogExample
    Project 1 - Library, Name: Log Expiration Functions that Call NLog
    Project 2 - Library, Name: MyLibrary - Used to create a demo DLL that uses the Log function.
    Project 3 - Console Application, Name: Main - used to create a demo EXE using the Log functions.

  • and. Manually create NLog.config
    b. Accessing NLog.config from a running project
    with. Write message to file

  • and. Create a configuration programmatically
    b. Create config for running project and register message in file

  • Create config and register message to file using F # Interactive

1. Build a solution with three projects and install NLog

Using Visual Studio creates three projects.

enter image description here

Install NLog for all three projects.

enter image description here

2.a. Manually create NLog.config

Note. For these examples to work, when __SOURCE_DIRECTORY__;;

launched from F # Interactive, it must report a directory that is part of the project and NOT a directory Temp

.

Note. All paths in this answer refer to the solution directory.
When you see a <Solution directory>

replacement in your actual solution directory.

Way: <Solution director>\NLog.config

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwExceptions="true">

  <targets>
    <target xsi:type="File"
            name="file"
            fileName="<Solution directory>\log.txt"
            autoFlush="true"
      />
    </targets>

  <rules>
    <logger name="*"
            minlevel="Trace"
            writeTo="file"
    />
  </rules>
</nlog>

      

Note. Remember to change <Solution directory>

to the actual path and setautoFlush="true"

Note. Adding NLog.config

to the solution makes it easier to view / modify the file.

enter image description here

2b. Accessing NLog.config from a running project

In Log.Library1.fs



namespace Log

module MyLog =

    let configureNLog () =
      let projectPath = __SOURCE_DIRECTORY__
      let soulutionPath = projectPath + "\.."
      let configPath = soulutionPath + @"\NLog.config"
      let xmlConfig = new NLog.Config.XmlLoggingConfiguration(configPath)
      NLog.LogManager.Configuration <- xmlConfig

    let NLogConfigToString () = 
      let targets = NLog.LogManager.Configuration.AllTargets
      let out = ""
      let out = Seq.fold (fun out target -> out + (sprintf "%A\n" target)) out targets
      let rules = NLog.LogManager.Configuration.LoggingRules
      let out = Seq.fold (fun out rule -> out + (sprintf "%A\n" rule)) out rules
      out

    let printNLogConfig () = 
       Printf.printfn "%s" (NLogConfigToString ())

      

and for the log project add a link to System.XML

In Main.Program.fs

open Log

[<EntryPoint>]
let main argv = 

    MyLog.configureNLog ()
    MyLog.printNLogConfig ()

    0 // return an integer exit code

      

and for the main project, add a project reference Log

and set the main project as a startup project.

This should output to the console on startup:

File Target[file]
logNamePattern: (:All) levels: [ Trace Debug Info Warn Error Fatal ] appendTo: [ file ]

      

2.c. Write message to file

In Log.Library1.fs



namespace Log

open NLog

module MyLog =

    let configureNLog () =
      let projectPath = __SOURCE_DIRECTORY__
      let soulutionPath = projectPath + "\.."
      let configPath = soulutionPath + @"\NLog.config"
      let xmlConfig = new NLog.Config.XmlLoggingConfiguration(configPath)
      NLog.LogManager.Configuration <- xmlConfig

    let NLogConfigToString () = 
      let targets = NLog.LogManager.Configuration.AllTargets
      let out = ""
      let out = Seq.fold (fun out target -> out + (sprintf "%A\n" target)) out targets
      let rules = NLog.LogManager.Configuration.LoggingRules
      let out = Seq.fold (fun out rule -> out + (sprintf "%A\n" rule)) out rules
      out

    let printNLogConfig () = 
       Printf.printfn "%s" (NLogConfigToString ())

    let evalTracer = LogManager.GetLogger("file")

      

In Main.Program.fs

open Log
open Library1

[<EntryPoint>]

    let main argv = 

        MyLog.configureNLog ()
        MyLog.printNLogConfig ()

        // Add as many of these as needed
        MyLog.evalTracer.Trace("In Main @1.")

        MyFunctions.test001 ()

        0 // return an integer exit code

      

and for the main project add a project reference MyLibrary

.

In MyLibrary.Library1.fs

namespace Library1

open Log

module MyFunctions =

    let test001 () =
        MyLog.evalTracer.Trace("In Library @1.")

      

and for the MyLibrary project add a project reference Log

.

When running, the log file log.txt

should contain something similar to:

2016-03-28 11:03:52.4963|TRACE|file|In Main @1.
2016-03-28 11:03:52.5263|TRACE|file|In Library @1

      

3.a. Create a config programmatically

If the file exists NLog.config

, delete it to ensure that the code created a new configuration but did not create the file.

To set up a configuration programmatically using F #, you need to know:

  • This FileName string represents a layout, which can include layout render instances. This allows one target to be used to write to multiple files.
  • SimpleLayout - Represents a string with inline placeholders that can display contextual information.

In Log.Library1.fs add

let configureNLogPrgramatically () =
  let config = new NLog.Config.LoggingConfiguration()
  let fileTarget = new NLog.Targets.FileTarget()
  let projectPath = __SOURCE_DIRECTORY__
  let soulutionPath = projectPath + "\.."
  let filePath = soulutionPath + @"\log.txt"
  let layout = new NLog.Layouts.SimpleLayout(filePath)
  fileTarget.Name <- "file"
  fileTarget.FileName <- layout
  fileTarget.AutoFlush <- true
  config.AddTarget("file", fileTarget)
  let rule1 = new NLog.Config.LoggingRule("*",NLog.LogLevel.Trace,fileTarget)
  config.LoggingRules.Add(rule1)
  NLog.LogManager.Configuration <- config

      

3.b. Create config for running project and write message to file

In Main.Program.fs

open Log
open Library1

[<EntryPoint>]

    let main argv = 

        MyLog.configureNLogPrgramatically ()
        MyLog.printNLogConfig ()

        // Add as many of these as needed
        MyLog.evalTracer.Trace("In Main @1.")

        MyFunctions.test001 ()

        0 // return an integer exit code

      

When running, the log file log.txt

should contain something similar to:

2016-03-28 11:16:07.2901|TRACE|file|In Main @1.
2016-03-28 11:16:07.3181|TRACE|file|In Library @1.

      

and note that the file NLog.config

was NOT .

4. Create config and register message to file using F # Interactive

In MyLibrary.Script.fsx

// print out __SOURCE_DIRECTORY__ to make sure we are not using the Temp directory
printfn __SOURCE_DIRECTORY__

#I __SOURCE_DIRECTORY__

// Inform F# Interactive where to find functions in Log module
#I "../Log/bin/Debug/"
#r "Log.dll"

open Log

// Functions in Log module can now be run.
MyLog.configureNLogPrgramatically ()
MyLog.printNLogConfig ()

// Inform F# Interactive where to find functions in MyLibrary module
#I "../MyLibrary/bin/Debug/"
#r "MyLibrary.dll"

open Library1

// Functions in MyLibrary module can now be run.
MyFunctions.test001 ()

      

When a script is executed with F # Interactive

Microsoft (R) F# Interactive version 14.0.23413.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

For help type #help;;

> 
<Solution directory>\MyLibrary
val it : unit = ()

--> Added <Solution directory>\MyLibrary' to library include path


--> Added <Solution directory>\MyLibrary\../Log/bin/Debug/' to library include path


--> Referenced <Solution directory>\MyLibrary\../Log/bin/Debug/Log.dll'

File Target[file]
logNamePattern: (:All) levels: [ Trace Debug Info Warn Error Fatal ] appendTo: [ file ]


--> Added <Solution directory>\MyLibrary\../MyLibrary/bin/Debug/' to library include path


--> Referenced <Solution directory>\MyLibrary\../MyLibrary/bin/Debug/MyLibrary.dll'


val it : unit = ()

> 

      

The log file log.txt

should contain something similar to:

2016-03-28 11:42:41.5417|TRACE|file|In Library @1.

      

Also, this will log while you still have an active F # Interactive session, so you can look into the log between command executions.

+10


source







All Articles