diff --git a/Exemplars/StandardFunctions/CosmosExample.cs b/Exemplars/StandardFunctions/CosmosExample.cs index 92634cff..1fb2bbd5 100644 --- a/Exemplars/StandardFunctions/CosmosExample.cs +++ b/Exemplars/StandardFunctions/CosmosExample.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Net.Http.Formatting; @@ -22,20 +23,29 @@ public static void Run([CosmosDBTrigger( ConnectionStringSetting = "cosmosConnectionString", LeaseCollectionName = "leases")]IReadOnlyList input, ILogger log) { + //Lazy errorHandler = new Lazy(() => string.Empty); foreach (Document document in input) { - document.GetPropertyValue("a"); - using (MemoryStream memoryStream = new MemoryStream()) + try { - document.SaveTo(memoryStream, SerializationFormattingPolicy.None, new JsonSerializerSettings + + + document.GetPropertyValue("a"); + using (MemoryStream memoryStream = new MemoryStream()) { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }); - string json = Encoding.UTF8.GetString(memoryStream.ToArray()); - string myCommand = JsonConvert.DeserializeObject(json); - } + document.SaveTo(memoryStream, SerializationFormattingPolicy.None, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + string json = Encoding.UTF8.GetString(memoryStream.ToArray()); + string myCommand = JsonConvert.DeserializeObject(json); + } + } + catch + { + break; + } } - if (input != null && input.Count > 0) { log.LogInformation("Documents modified " + input.Count); diff --git a/Samples/Scratch/SwaggerBuildOut/ExampleCosmosErrorHandler.cs b/Samples/Scratch/SwaggerBuildOut/ExampleCosmosErrorHandler.cs new file mode 100644 index 00000000..1b685079 --- /dev/null +++ b/Samples/Scratch/SwaggerBuildOut/ExampleCosmosErrorHandler.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using FunctionMonkey.Commanding.Cosmos.Abstractions; +using Microsoft.Azure.Documents; +using Microsoft.Build.Framework; +using Microsoft.Extensions.Logging; + +namespace SwaggerBuildOut +{ + public class ExampleCosmosErrorHandler : ICosmosDbErrorHandler + { + private readonly ILogger _logger; + + public ExampleCosmosErrorHandler(ILogger logger) + { + _logger = logger; + } + + public Task HandleError(Exception ex, Document document) + { + _logger.LogError(ex, "Went wrong"); + return Task.FromResult(true); + } + } +} diff --git a/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs b/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs index 037d3fbd..b542a680 100644 --- a/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs +++ b/Samples/Scratch/SwaggerBuildOut/FunctionAppConfiguration.cs @@ -25,13 +25,15 @@ public void Build(IFunctionHostBuilder builder) commandRegistry.Register(); commandRegistry.Register(); commandRegistry.Register(); + + serviceCollection.AddLogging(); }) .OpenApiEndpoint(openApi => openApi .Title("A Simple API") .Version("0.0.0") .UserInterface() ) - .OutputAuthoredSource(@"d:\wip\scratch\outputSource") + .OutputAuthoredSource(@"c:\wip\scratch\outputSource") .Functions(functions => functions .HttpRoute("/HelloWorld", route => route .HttpFunction("/{name}", AuthorizationTypeEnum.Anonymous, HttpMethod.Get) @@ -41,9 +43,9 @@ public void Build(IFunctionHostBuilder builder) ) .OpenApiDescription("A route description") .CosmosDb("CosmosConnection", cosmos => cosmos - .ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn1")//, convertToPascalCase:true) + .ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn1")//, convertToPascalCase:true) //.ChangeFeedFunction("Items", "ToDoList") - .ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn2") + //.ChangeFeedFunction("Items", "ToDoList", leaseCollectionPrefix:"fn2") ) /*.HttpRoute("/Add", route => route .HttpFunction(AuthorizationTypeEnum.Anonymous,HttpMethod.Post) diff --git a/Samples/Scratch/SwaggerBuildOut/Handlers/CosmosCommandHandler.cs b/Samples/Scratch/SwaggerBuildOut/Handlers/CosmosCommandHandler.cs index 3a727c49..ec6903b8 100644 --- a/Samples/Scratch/SwaggerBuildOut/Handlers/CosmosCommandHandler.cs +++ b/Samples/Scratch/SwaggerBuildOut/Handlers/CosmosCommandHandler.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using AzureFromTheTrenches.Commanding.Abstractions; using SwaggerBuildOut.Commands; @@ -8,6 +9,7 @@ class CosmosCommandHandler : ICommandHandler { public Task ExecuteAsync(CosmosCommand command) { + throw new Exception("eeek"); return Task.CompletedTask; } } diff --git a/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs b/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs index 11f48948..09c08a6e 100644 --- a/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs +++ b/Source/FunctionMonkey.Abstractions/Builders/ICosmosDbFunctionBuilder.cs @@ -1,4 +1,5 @@ using AzureFromTheTrenches.Commanding.Abstractions; +using FunctionMonkey.Commanding.Cosmos.Abstractions; namespace FunctionMonkey.Abstractions.Builders { @@ -52,5 +53,55 @@ ICosmosDbFunctionBuilder ChangeFeedFunction(string collectionName, int? leasesCollectionThroughput=null ) where TCommand : ICommand; + + /// + /// Associate a function with the specified collection and database + /// + /// + /// A type that provides error handling for document processing + /// The Cosmos collection name + /// The Cosmos database name + /// The cosmos collection to use for leases - defaults to leases + /// The cosmos database to use for leases - defaults to the database name + /// Creates the lease collection if it doesn't exist, defaults to false + /// Start from the beginning of the change feed, defaults to false + /// + /// Is the Cosmos document in camel case. Defaults to true in which case C# property names will be converted to camel case. Note that + /// due to limitations in the current Functions / Cosmos Change Feed Processor this involves serializing to a memory stream and then + /// deserializing. + /// + /// If this limitation continues then I will seek to optimize this by compiling camel case model classes and mapping them to pascal + /// case C# classes via Roslyn. + /// + /// Alternatively you can use the JsonProperty attribute on all your properties. Messy I know. + /// + /// Due to the potential expense this defaults to false. + /// + /// When set, it adds a prefix to the leases created in the Lease collection for this Function + /// When set, it customizes the maximum amount of items received per Function call. + /// When set, it defines, in milliseconds, the delay in between polling a partition for new changes on the feed, after all current changes are drained. Default is 5000 (5 seconds). + /// When set, it defines, in milliseconds, the interval to kick off a task to compute if partitions are distributed evenly among known host instances. Default is 13000 (13 seconds). + /// When set, it defines, in milliseconds, the interval for which the lease is taken on a lease representing a partition. If the lease is not renewed within this interval, it will cause it to expire and ownership of the partition will move to another instance. Default is 60000 (60 seconds) + /// When set, it defines, in milliseconds, the renew interval for all leases for partitions currently held by an instance. Default is 17000 (17 seconds). + /// When set, it defines, in milliseconds, the interval between lease checkpoints. Default is always after a successful Function call. + /// Defines the amount of Request Units to assign when the leases collection is created. This setting is only used When createLeaseCollectionIfNotExists is set to true. This parameter is automatically set when the binding is created using the portal. + /// + ICosmosDbFunctionBuilder ChangeFeedFunction(string collectionName, + string databaseName, + string leaseCollectionName = "leases", + string leaseDatabaseName = null, + bool createLeaseCollectionIfNotExists = false, + bool startFromBeginning = false, + bool convertToPascalCase = false, + string leaseCollectionPrefix = null, + int? maxItemsPerInvocation = null, + int? feedPollDelay = null, + int? leaseAcquireInterval = null, + int? leaseExpirationInterval = null, + int? leaseRenewInterval = null, + int? checkpointFrequency = null, + int? leasesCollectionThroughput = null + ) + where TCommand : ICommand where TCosmosDbErrorHandler : ICosmosDbErrorHandler; } } diff --git a/Source/FunctionMonkey.Abstractions/FunctionMonkey.Abstractions.csproj b/Source/FunctionMonkey.Abstractions/FunctionMonkey.Abstractions.csproj index 2d62e90b..14736942 100644 --- a/Source/FunctionMonkey.Abstractions/FunctionMonkey.Abstractions.csproj +++ b/Source/FunctionMonkey.Abstractions/FunctionMonkey.Abstractions.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.16.0-beta000 + 0.16.1-beta000 James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ @@ -12,6 +12,7 @@ + diff --git a/Source/FunctionMonkey.Abstractions/ICosmosDbErrorHandler.cs b/Source/FunctionMonkey.Abstractions/ICosmosDbErrorHandler.cs new file mode 100644 index 00000000..d1cda904 --- /dev/null +++ b/Source/FunctionMonkey.Abstractions/ICosmosDbErrorHandler.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Azure.Documents; + +namespace FunctionMonkey.Commanding.Cosmos.Abstractions +{ + /// + /// Implementations of this handle Cosmos errors at the Function level. + /// + public interface ICosmosDbErrorHandler + { + /// + /// Handle a error processing a Cosmos document. + /// To abort processing of the current batch return false. + /// + /// The exception that caused the error + /// The document that caused the error + /// True to continue processing, false to stop processing of the batch + Task HandleError(Exception ex, Document document); + } +} diff --git a/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj b/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj index afbc61d7..bb662b27 100644 --- a/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj +++ b/Source/FunctionMonkey.Commanding.Abstractions/FunctionMonkey.Commanding.Abstractions.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.16.0-beta000 + 0.16.1-beta000 James Randall James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git - 0.16.0.0 - 0.16.0.0 + 0.16.1.0 + 0.16.1.0 diff --git a/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj b/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj index ef36b282..11eae98e 100644 --- a/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj +++ b/Source/FunctionMonkey.Commanding.Cosmos.Abstractions/FunctionMonkey.Commanding.Cosmos.Abstractions.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.16.0-beta000 + 0.16.1-beta000 diff --git a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj index 7b4f36de..54181d9d 100644 --- a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj +++ b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.csproj @@ -3,7 +3,7 @@ Exe netcoreapp2.0 - 0.16.0-beta000 + 0.16.1-beta000 FunctionMonkey.Compiler FunctionMonkey.Compiler @@ -358,8 +358,8 @@ $(MSBuildProjectDirectory)/bin/$(Configuration)/publish/ $(IntermediatePackDir)$(TargetFramework)/ publishDir=$([MSBuild]::NormalizeDirectory($(IntermediatePackDir))) - 0.16.0.0 - 0.16.0.0 + 0.16.1.0 + 0.16.1.0 diff --git a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec index 730c96c8..7c6d5ae2 100644 --- a/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec +++ b/Source/FunctionMonkey.Compiler/FunctionMonkey.Compiler.nuspec @@ -2,7 +2,7 @@ FunctionMonkey.Compiler - 0.16.0-beta000 + 0.16.1-beta000 James Randall Generates Azure Functions from command registrations https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE diff --git a/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars b/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars index de5ede2e..3e274a51 100644 --- a/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars +++ b/Source/FunctionMonkey.Compiler/Templates/cosmosdb.csharp.handlebars @@ -41,33 +41,46 @@ namespace {{Namespace}} var result = await FunctionMonkey.Runtime.CommandDispatcher.DispatchAsync(command); // The result will be useful when we want to include outputs too {{else}} + {{#if ErrorHandlerTypeName}} + Lazy<{{ErrorHandlerTypeName}}> errorHandler = new Lazy<{{ErrorHandlerTypeName}}>( + () => ({{ErrorHandlerTypeName}})FunctionMonkey.Runtime.ServiceProvider.GetService(typeof({{ErrorHandlerTypeName}})) + ); + {{/if}} foreach(Document document in input) { - {{CommandTypeName}} command = null; - {{#if IsDocumentCommand}} - command = new {{CommandTypeName}}(); - command.Document = document; - {{else}} - {{#if ConvertToPascalCase}} - using (MemoryStream memoryStream = new MemoryStream()) - { - document.SaveTo(memoryStream, SerializationFormattingPolicy.None, new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }); - string json = Encoding.UTF8.GetString(memoryStream.ToArray()); - command = JsonConvert.DeserializeObject<{{CommandTypeName}}>(json); - } - {{else}} + try + { + {{CommandTypeName}} command = null; + {{#if IsDocumentCommand}} command = new {{CommandTypeName}}(); - {{#each CommandProperties}} - command.{{Name}} = document.GetPropertyValue<{{TypeName}}>("{{CosmosPropertyName}}"); - {{/each}} + command.Document = document; + {{else}} + {{#if ConvertToPascalCase}} + string json = document.ToString(); + command = JsonConvert.DeserializeObject<{{CommandTypeName}}>(json); + {{else}} + command = new {{CommandTypeName}}(); + {{#each CommandProperties}} + command.{{Name}} = document.GetPropertyValue<{{TypeName}}>("{{CosmosPropertyName}}"); + {{/each}} + {{/if}} {{/if}} - {{/if}} - var result = await FunctionMonkey.Runtime.CommandDispatcher.DispatchAsync(command); - // The result will be useful when we want to include outputs too + var result = await FunctionMonkey.Runtime.CommandDispatcher.DispatchAsync(command); + // The result will be useful when we want to include outputs too + } + catch(Exception ex) + { + {{#if ErrorHandlerTypeName}} + bool shouldContinue = await errorHandler.Value.HandleError(ex, document); + if (!shouldContinue) + { + break; + } + {{else}} + log.LogError(ex, "Error occurred processing document with id {Id}", document.Id); + {{/if}} + } } {{/if}} } diff --git a/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj b/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj index 56d4c310..ce1c9a31 100644 --- a/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj +++ b/Source/FunctionMonkey.FluentValidation/FunctionMonkey.FluentValidation.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.16.0-beta000 + 0.16.1-beta000 James Randall James Randall https://raw.githubusercontent.com/JamesRandall/AzureFromTheTrenches.Commanding/master/LICENSE https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git - 0.16.0.0 - 0.16.0.0 + 0.16.1.0 + 0.16.1.0 diff --git a/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs b/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs index 2cb3edbf..9074fa36 100644 --- a/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs +++ b/Source/FunctionMonkey/Builders/CosmosDbFunctionBuilder.cs @@ -4,6 +4,8 @@ using AzureFromTheTrenches.Commanding.Abstractions; using FunctionMonkey.Abstractions; using FunctionMonkey.Abstractions.Builders; +using FunctionMonkey.Commanding.Cosmos.Abstractions; +using FunctionMonkey.Extensions; using FunctionMonkey.Model; namespace FunctionMonkey.Builders @@ -63,5 +65,47 @@ public ICosmosDbFunctionBuilder ChangeFeedFunction( }); return this; } + + public ICosmosDbFunctionBuilder ChangeFeedFunction( + string collectionName, + string databaseName, + string leaseCollectionName = "leases", + string leaseDatabaseName = null, + bool createLeaseCollectionIfNotExists = false, + bool startFromBeginning = false, + bool convertToPascalCase = false, + string leaseCollectionPrefix = null, + int? maxItemsPerInvocation = null, + int? feedPollDelay = null, + int? leaseAcquireInterval = null, + int? leaseExpirationInterval = null, + int? leaseRenewInterval = null, + int? checkpointFrequency = null, + int? leasesCollectionThroughput = null) where TCommand : ICommand where TCosmosDbErrorHandler : ICosmosDbErrorHandler + { + _functionDefinitions.Add(new CosmosDbFunctionDefinition(typeof(TCommand)) + { + ConnectionStringName = _connectionStringName, + CollectionName = collectionName, + DatabaseName = databaseName, + LeaseConnectionStringName = _leaseConnectionStringName, + LeaseCollectionName = leaseCollectionName, + LeaseDatabaseName = leaseDatabaseName ?? databaseName, + CreateLeaseCollectionIfNotExists = createLeaseCollectionIfNotExists, + ConvertToPascalCase = convertToPascalCase, + StartFromBeginning = startFromBeginning, + LeaseCollectionPrefix = leaseCollectionPrefix, + MaxItemsPerInvocation = maxItemsPerInvocation, + FeedPollDelay = feedPollDelay, + LeaseAcquireInterval = leaseAcquireInterval, + LeaseExpirationInterval = leaseExpirationInterval, + LeaseRenewInterval = leaseRenewInterval, + CheckpointFrequency = checkpointFrequency, + LeasesCollectionThroughput = leasesCollectionThroughput, + ErrorHandlerType = typeof(TCosmosDbErrorHandler), + ErrorHandlerTypeName = typeof(TCosmosDbErrorHandler).EvaluateType() + }); + return this; + } } } diff --git a/Source/FunctionMonkey/FunctionMonkey.csproj b/Source/FunctionMonkey/FunctionMonkey.csproj index d0987999..7660b264 100644 --- a/Source/FunctionMonkey/FunctionMonkey.csproj +++ b/Source/FunctionMonkey/FunctionMonkey.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.16.0-beta000 + 0.16.1-beta000 false James Randall @@ -10,8 +10,8 @@ https://commanding.azurefromthetrenches.com/ https://github.com/JamesRandall/FunctionMonkey.git true - 0.16.0.0 - 0.16.0.0 + 0.16.1.0 + 0.16.1.0 diff --git a/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs b/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs index 528ac007..977a0de6 100644 --- a/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs +++ b/Source/FunctionMonkey/Model/CosmosDbFunctionDefinition.cs @@ -44,5 +44,7 @@ public CosmosDbFunctionDefinition(Type commandType) : base("CosmosFn", commandTy public int? LeaseRenewInterval { get; set; } public int? CheckpointFrequency { get; set; } public int? LeasesCollectionThroughput { get; set; } + public string ErrorHandlerTypeName { get; set; } + public Type ErrorHandlerType { get; set; } } } diff --git a/Source/FunctionMonkey/Runtime.cs b/Source/FunctionMonkey/Runtime.cs index 8c7784f6..b019ee3e 100644 --- a/Source/FunctionMonkey/Runtime.cs +++ b/Source/FunctionMonkey/Runtime.cs @@ -66,11 +66,34 @@ static Runtime() RegisterTimerCommandFactories(builder.FunctionDefinitions); RegisterHttpDependencies(builder.FunctionDefinitions); + + RegisterCosmosDependencies(builder.FunctionDefinitions); ServiceProvider = containerProvider.CreateServiceProvider(ServiceCollection); builder.ServiceProviderCreatedAction?.Invoke(ServiceProvider); } + private static void RegisterCosmosDependencies( + IReadOnlyCollection builderFunctionDefinitions) + { + HashSet types = new HashSet(); + foreach (AbstractFunctionDefinition abstractFunctionDefinition in builderFunctionDefinitions) + { + if (abstractFunctionDefinition is CosmosDbFunctionDefinition cosmosFunctionDefinition) + { + if (cosmosFunctionDefinition.ErrorHandlerType != null) + { + types.Add(cosmosFunctionDefinition.ErrorHandlerType); + } + } + } + + foreach (Type claimsPrincipalAuthorizationType in types) + { + ServiceCollection.AddTransient(claimsPrincipalAuthorizationType); + } + } + private static void RegisterHttpDependencies(IReadOnlyCollection builderFunctionDefinitions) { HashSet types = new HashSet(); diff --git a/docfx/guides/cosmos/errorHandling.md b/docfx/guides/cosmos/errorHandling.md new file mode 100644 index 00000000..9a545d4f --- /dev/null +++ b/docfx/guides/cosmos/errorHandling.md @@ -0,0 +1,47 @@ +# Error Handling + +Unless you use the _ICosmosDbDocumentBatchCommand_ interface on your commands then Function Monkey will process batches of documents in the order they are recieved - one invocation of your function may contain multiple documents. + +If an error occurs, either in the Function Monkey wrapper or due to an exception thrown from your command handler, then the default behaviour is to log an error along with the document ID and continue to process documents. + +You can extend and modify this behaviour by implementing and registering an instance of the _ICosmosDbErrorHandler_ interface. This will allow you to take additional steps (for example initiate some form of retry), log information yourself, and stop the processing of the current batch. + +The example implementation below logs a warning and allows processing of the current batch to continue: + + public class ExampleCosmosErrorHandler : ICosmosDbErrorHandler + { + private readonly ILogger _logger; + + public ExampleCosmosErrorHandler(ILogger logger) + { + _logger = logger; + } + + public Task HandleError(Exception ex, Document document) + { + _logger.LogWarning(ex, "Ooops"); + return Task.FromResult(true); // return false to stop the processing of the current batch + } + } + +This can be registered on a per function basis as shown in the example configuration block below: + + public class FunctionAppConfiguration : IFunctionAppConfiguration + { + private const string CosmosDatabaseName = "Financials"; + private const string CosmosCollectionName = "Invoices"; + private const string CosmosConnectionSettingName = "cosmosDbConnection"; + + public void Build(IFunctionHostBuilder builder) + { + builder + .Setup((serviceCollection, commandRegistry) => + commandRegistry.Register() + ) + .Functions(functions => functions + .CosmosDb(CosmosConnectionSettingName, cosmos => cosmos + .ChangeFeedFunction(CosmosCollectionName, CosmosDatabaseName, convertToPascalCase:true) + ) + ); + } + } diff --git a/docfx/guides/crosscutting/logging.md b/docfx/guides/crosscutting/logging.md new file mode 100644 index 00000000..6bf66e06 --- /dev/null +++ b/docfx/guides/crosscutting/logging.md @@ -0,0 +1,21 @@ +# Logging + +To add logging support simply register the appropriate dependencies in the _Setup_ method of your builder. For example to add the Microsoft logging framework and make _ILogger_ and _ILogger_ available to your command handlers use a block similar to the below: + + public class FunctionAppConfiguration : IFunctionAppConfiguration + { + public void Build(IFunctionHostBuilder builder) + { + builder + .Setup((serviceCollection, commandRegistry) => + { + commandRegistry.Register(); + serviceCollection.AddLogging(); + }) + .Functions(functions => functions + .HttpRoute("/api/v1/HelloWorld", route => route + .HttpFunction() + ) + ); + } + } diff --git a/docfx/guides/toc.yml b/docfx/guides/toc.yml index d38951ab..8686130a 100644 --- a/docfx/guides/toc.yml +++ b/docfx/guides/toc.yml @@ -36,8 +36,12 @@ href: serviceBus/topics.md - name: Message Properties href: serviceBus/messageProperties.md - - name: Cosmos DB Triggers - href: cosmos/changeFeed.md + - name: Cosmos DB + items: + - name: Overview + href: cosmos/changeFeed.md + - name: Error Handling + href: cosmos/errorHandling.md - name: Timer Triggers href: timers/timers.md - name: Azure Storage Triggers @@ -53,6 +57,8 @@ items: - name: IoC Containers href: crosscutting/iocContainers.md + - name: Logging + href: crosscutting/logging.md - name: Trigger Contexts href: crosscutting/triggercontexts.md - name: Validation