Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

module Logging -> type #76

Merged
merged 12 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The `Unreleased` section name is replaced by the expected version of next releas
### Added
### Changed

- Cleaned and moved `Logging` logic out to `Infrastructure.fs` [#76](https://github.com/jet/dotnet-templates/pull/76)
- Polished `SemaphoreSlim` extensions [#79](https://github.com/jet/dotnet-templates/pull/79)

### Removed
Expand Down
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ _Responsible for: Loading secrets and custom configuration, supplying defaults w

Wiring up retrieval of configuration values is the most environment-dependent aspect of the wiring up of an application's interaction with its environment and/or data storage mechanisms. This is particularly relevant where there is variance between local (development time), testing and production deployments. For this reason, the retrieval of values from configuration stores or key vaults is not managed directly within the [`module Args` section](#module-args)

The `Settings` module is responsible for the following:
The `Configuration` module is responsible for the following:
1. Feeding defaults into process-local Environment Variables, _where those are not already supplied_
2. Encapsulating all bindings to Configuration or Secret stores (Vaults) in order that this does not have to be complected with the argument parsing or defaulting in `module Args`

Expand Down Expand Up @@ -249,7 +249,7 @@ _Responsible for: mapping Environment Variables and the Command Line `argv` to a

NOTE: there's a [medium term plan to submit a PR to Argu](https://github.com/fsprojects/Argu/issues/143) extending it to be able to fall back to environment variables where a value is not supplied, by means of declarative attributes on the Argument specification in the DU, _including having the `--help` message automatically include a reference to the name of the environment variable that one can supply the value through_

### `module Logging`
### `type Logging`

_Responsible for applying logging config and setting up loggers for the application_

Expand All @@ -258,10 +258,15 @@ _Responsible for applying logging config and setting up loggers for the applicat
#### example

```
module Logging =

let initialize verbose =
Log.Logger <- LoggerConfiguration(….)
type Logging() =

[<Extension>]
static member Configure(configuration : LoggingConfiguration, ?verbose) =
configuration
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose = Some true then c.MinimumLevel.Debug() else c
// etc.
```

### `start` function
Expand Down Expand Up @@ -290,17 +295,18 @@ The `run` function formalizes the overall pattern. It is responsible for:
#### example

```
let run args =
let run args = async {
use consumer = start args
consumer.AwaitCompletion() |> Async.RunSynchronously
consumer.RanToCompletion
return! consumer.AwaitCompletion()
}

[<EntryPoint>]
let main argv =
try let args = Args.parse argv
try Logging.initialize args.Verbose
try Log.Logger <- LoggerConfiguration().Configure(verbose=args.Verbose).CreateLogger()
try Configuration.initialize ()
if run args then 0 else 3
run args |> Async.RunSynchronously
0
with e when not (e :? Args.MissingArg) -> Log.Fatal(e, "Exiting"); 2
finally Log.CloseAndFlush()
with Args.MissingArg msg -> eprintfn "%s" msg; 1
Expand Down
37 changes: 37 additions & 0 deletions equinox-shipping/Watchdog/Infrastructure.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[<AutoOpen>]
module Shipping.Watchdog.Infrastructure

open Serilog
open System.Runtime.CompilerServices

[<Extension>]
type LoggerConfigurationExtensions() =

[<Extension>]
static member inline ExcludeChangeFeedProcessorV2InternalDiagnostics(c : LoggerConfiguration) =
let isCfp429a = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.LeaseManagement.DocumentServiceLeaseUpdater").Invoke
let isCfp429b = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.LeaseRenewer").Invoke
let isCfp429c = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.PartitionLoadBalancer").Invoke
let isCfp429d = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.FeedProcessing.PartitionProcessor").Invoke
let isCfp x = isCfp429a x || isCfp429b x || isCfp429c x || isCfp429d x
c.Filter.ByExcluding(fun x -> isCfp x)

[<Extension>]
static member inline ConfigureChangeFeedProcessorLogging(c : LoggerConfiguration, verbose : bool) =
// LibLog writes to the global logger, so we need to control the emission
let cfpl = if verbose then Serilog.Events.LogEventLevel.Debug else Serilog.Events.LogEventLevel.Warning
c.MinimumLevel.Override("Microsoft.Azure.Documents.ChangeFeedProcessor", cfpl)
|> fun c -> if verbose then c else c.ExcludeChangeFeedProcessorV2InternalDiagnostics()

[<Extension>]
type Logging() =

[<Extension>]
static member Configure(configuration : LoggerConfiguration, ?verbose, ?changeFeedProcessorVerbose) =
configuration
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose = Some true then c.MinimumLevel.Debug() else c
|> fun c -> c.ConfigureChangeFeedProcessorLogging((changeFeedProcessorVerbose = Some true))
|> fun c -> let t = "[{Timestamp:HH:mm:ss} {Level:u3}] {partitionKeyRangeId,2} {Message:lj} {NewLine}{Exception}"
c.WriteTo.Console(theme=Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code, outputTemplate=t)
32 changes: 6 additions & 26 deletions equinox-shipping/Watchdog/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -175,27 +175,6 @@ module Args =
let parser = ArgumentParser.Create<Parameters>(programName=programName)
parser.ParseCommandLine argv |> Arguments

module Logging =

let initialize verbose changeFeedProcessorVerbose =
Log.Logger <-
LoggerConfiguration()
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose then c.MinimumLevel.Debug() else c
// LibLog writes to the global logger, so we need to control the emission
|> fun c -> let cfpl = if changeFeedProcessorVerbose then Serilog.Events.LogEventLevel.Debug else Serilog.Events.LogEventLevel.Warning
c.MinimumLevel.Override("Microsoft.Azure.Documents.ChangeFeedProcessor", cfpl)
|> fun c -> let isCfp429a = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.LeaseManagement.DocumentServiceLeaseUpdater").Invoke
let isCfp429b = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.LeaseRenewer").Invoke
let isCfp429c = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.PartitionLoadBalancer").Invoke
let isCfp429d = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.FeedProcessing.PartitionProcessor").Invoke
let isCfp x = isCfp429a x || isCfp429b x || isCfp429c x || isCfp429d x
if changeFeedProcessorVerbose then c else c.Filter.ByExcluding(fun x -> isCfp x)
|> fun c -> let t = "[{Timestamp:HH:mm:ss} {Level:u3}] {partitionKeyRangeId,2} {Message:lj} {NewLine}{Exception}"
c.WriteTo.Console(theme=Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code, outputTemplate=t)
|> fun c -> c.CreateLogger()

let [<Literal>] AppName = "Watchdog"

let startWatchdog log (processingTimeout, stats : Handler.Stats) (maxReadAhead, maxConcurrentStreams) driveTransaction
Expand Down Expand Up @@ -233,18 +212,19 @@ let build (args : Args.Arguments) =
?maxDocuments=maxDocuments, ?lagReportFreq=lagFrequency)
sink, pipeline

let run args =
let run args = async {
let sink, pipeline = build args
pipeline |> Async.Start
sink.AwaitCompletion() |> Async.RunSynchronously
sink.RanToCompletion
return! sink.AwaitCompletion()
}

[<EntryPoint>]
let main argv =
try let args = Args.parse argv
try Logging.initialize args.Verbose args.CfpVerbose
try Log.Logger <- LoggerConfiguration().Configure(verbose=args.Verbose, changeFeedProcessorVerbose=args.CfpVerbose).CreateLogger()
try Configuration.initialize ()
if run args then 0 else 3
run args |> Async.RunSynchronously
0
with e when not (e :? Args.MissingArg) -> Log.Fatal(e, "Exiting"); 2
finally Log.CloseAndFlush()
with Args.MissingArg msg -> eprintfn "%s" msg; 1
Expand Down
1 change: 1 addition & 0 deletions equinox-shipping/Watchdog/Watchdog.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<None Include="README.md" />
<Compile Include="Infrastructure.fs" />
<Compile Include="Handler.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion equinox-testbed/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ module Args =
#endif

let createStoreLog verbose verboseConsole maybeSeqEndpoint =
let c = LoggerConfiguration().Destructure.FSharpTypes()
let c = LoggerConfiguration()
.Destructure.FSharpTypes()
let c = if verbose then c.MinimumLevel.Debug() else c
//#if eventStore
let c = c.WriteTo.Sink(Equinox.EventStore.Log.InternalMetrics.Stats.LogSink())
Expand Down
18 changes: 11 additions & 7 deletions equinox-web-csharp/Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@

namespace TodoBackendTemplate.Web
{
static class Logging
{
public static LoggerConfiguration Configure(this LoggerConfiguration c) =>
c
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
// .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console();
}
static class Program
{
public static async Task<int> Main(string[] argv)
{
try
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
// .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
Log.Logger = new LoggerConfiguration().Configure().CreateLogger();
var host = WebHost
.CreateDefaultBuilder(argv)
.UseSerilog()
Expand Down
12 changes: 7 additions & 5 deletions equinox-web/Web/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ open Microsoft.AspNetCore
open Microsoft.AspNetCore.Hosting
open Serilog

let initLogging () =
Log.Logger <-
LoggerConfiguration()
[<System.Runtime.CompilerServices.Extension>]
type Logging() =

[<System.Runtime.CompilerServices.Extension>]
static member Configure(c : LoggerConfiguration) =
c
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft.AspNetCore", Serilog.Events.LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger()

let createWebHostBuilder args : IWebHostBuilder =
WebHost
Expand All @@ -21,7 +23,7 @@ let createWebHostBuilder args : IWebHostBuilder =

[<EntryPoint>]
let main argv =
try initLogging ()
try Log.Logger <- LoggerConfiguration().Configure().CreateLogger()
createWebHostBuilder(argv).Build().Run()
0
with e ->
Expand Down
15 changes: 14 additions & 1 deletion propulsion-consumer/Infrastructure.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[<AutoOpen>]
module private ConsumerTemplate.Infrastructure

open Serilog
open System
open System.Threading.Tasks

Expand Down Expand Up @@ -47,4 +48,16 @@ type System.Threading.SemaphoreSlim with
do! semaphore.Await()
try return! workflow
finally semaphore.Release() |> ignore
}
}

[<System.Runtime.CompilerServices.Extension>]
type Logging() =

[<System.Runtime.CompilerServices.Extension>]
static member Configure(configuration : LoggerConfiguration, ?verbose) =
configuration
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose = Some true then c.MinimumLevel.Debug() else c
|> fun c -> let theme = Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code
c.WriteTo.Console(theme=theme, outputTemplate="[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties}{NewLine}{Exception}")
25 changes: 6 additions & 19 deletions propulsion-consumer/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,6 @@ module Args =
let parser = ArgumentParser.Create<Parameters>(programName = programName)
parser.ParseCommandLine argv |> Arguments

// TODO remove this entire comment after reading https://github.com/jet/dotnet-templates#module-logging
// Application logic assumes the global `Serilog.Log` is initialized _immediately_ after a successful ArgumentParser.ParseCommandline
module Logging =

let initialize verbose =
Log.Logger <-
LoggerConfiguration()
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose then c.MinimumLevel.Debug() else c
|> fun c -> let theme = Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code
c.WriteTo.Console(theme=theme, outputTemplate="[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties}{NewLine}{Exception}")
|> fun c -> c.CreateLogger()

let [<Literal>] AppName = "ConsumerTemplate"

let start (args : Args.Arguments) =
Expand All @@ -104,17 +90,18 @@ let start (args : Args.Arguments) =
//NultiMessages.Parallel.Start(c, args.MaxDop)
MultiStreams.start(c, args.MaxDop)

let run args =
let run args = async {
use consumer = start args
consumer.AwaitCompletion() |> Async.RunSynchronously
consumer.RanToCompletion
return! consumer.AwaitCompletion()
}

[<EntryPoint>]
let main argv =
try let args = Args.parse argv
try Logging.initialize args.Verbose
try Log.Logger <- LoggerConfiguration().Configure(verbose=args.Verbose).CreateLogger()
try Configuration.initialize ()
if run args then 0 else 3
run args |> Async.RunSynchronously
0
with e when not (e :? Args.MissingArg) -> Log.Fatal(e, "Exiting"); 2
finally Log.CloseAndFlush()
with Args.MissingArg msg -> eprintfn "%s" msg; 1
Expand Down
45 changes: 45 additions & 0 deletions propulsion-projector/Infrastructure.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[<AutoOpen>]
module ProjectorTemplate.Infrastructure

open Serilog
open System.Runtime.CompilerServices

#if cosmos
[<Extension>]
type LoggerConfigurationExtensions() =

[<Extension>]
static member inline ExcludeChangeFeedProcessorV2InternalDiagnostics(c : LoggerConfiguration) =
let isCfp429a = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.LeaseManagement.DocumentServiceLeaseUpdater").Invoke
let isCfp429b = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.LeaseRenewer").Invoke
let isCfp429c = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.PartitionManagement.PartitionLoadBalancer").Invoke
let isCfp429d = Filters.Matching.FromSource("Microsoft.Azure.Documents.ChangeFeedProcessor.FeedProcessing.PartitionProcessor").Invoke
let isCfp x = isCfp429a x || isCfp429b x || isCfp429c x || isCfp429d x
c.Filter.ByExcluding(fun x -> isCfp x)

[<Extension>]
static member inline ConfigureChangeFeedProcessorLogging(c : LoggerConfiguration, verbose : bool) =
// LibLog writes to the global logger, so we need to control the emission
let cfpl = if verbose then Serilog.Events.LogEventLevel.Debug else Serilog.Events.LogEventLevel.Warning
c.MinimumLevel.Override("Microsoft.Azure.Documents.ChangeFeedProcessor", cfpl)
|> fun c -> if verbose then c else c.ExcludeChangeFeedProcessorV2InternalDiagnostics()

#endif
[<Extension>]
type Logging() =

[<Extension>]
#if cosmos
static member Configure(configuration : LoggerConfiguration, ?verbose, ?changeFeedProcessorVerbose) =
#else
static member Configure(configuration : LoggerConfiguration, ?verbose) =
#endif
configuration
.Destructure.FSharpTypes()
.Enrich.FromLogContext()
|> fun c -> if verbose = Some true then c.MinimumLevel.Debug() else c
#if cosmos
|> fun c -> c.ConfigureChangeFeedProcessorLogging((changeFeedProcessorVerbose = Some true))
#endif
|> fun c -> let t = "[{Timestamp:HH:mm:ss} {Level:u3}] {partitionKeyRangeId,2} {Message:lj} {NewLine}{Exception}"
c.WriteTo.Console(theme=Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code, outputTemplate=t)
Loading