adding initial ILogger/AppInsights support
This commit is contained in:
Родитель
c533e4cf67
Коммит
61aa424616
|
@ -1,23 +1,23 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
|
||||||
</startup>
|
</startup>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using Microsoft.Azure.WebJobs;
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace SampleHost
|
namespace SampleHost
|
||||||
{
|
{
|
||||||
|
@ -19,6 +22,24 @@ namespace SampleHost
|
||||||
config.UseDevelopmentSettings();
|
config.UseDevelopmentSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.Tracing.ConsoleLevel = TraceLevel.Off;
|
||||||
|
|
||||||
|
// Build up a LoggerFactory to log to App Insights, but only if this key exists.
|
||||||
|
string instrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
|
||||||
|
if (!string.IsNullOrEmpty(instrumentationKey))
|
||||||
|
{
|
||||||
|
// build up log filters
|
||||||
|
LogCategoryFilter logCategoryFilter = new LogCategoryFilter();
|
||||||
|
logCategoryFilter.DefaultLevel = LogLevel.Debug;
|
||||||
|
logCategoryFilter.CategoryLevels[LogCategories.Function] = LogLevel.Debug;
|
||||||
|
logCategoryFilter.CategoryLevels[LogCategories.Results] = LogLevel.Debug;
|
||||||
|
logCategoryFilter.CategoryLevels[LogCategories.Aggregator] = LogLevel.Debug;
|
||||||
|
|
||||||
|
config.LoggerFactory = new LoggerFactory()
|
||||||
|
.AddApplicationInsights(instrumentationKey, logCategoryFilter.Filter)
|
||||||
|
.AddConsole(logCategoryFilter.Filter);
|
||||||
|
}
|
||||||
|
|
||||||
config.CreateMetadataProvider().DebugDumpGraph(Console.Out);
|
config.CreateMetadataProvider().DebugDumpGraph(Console.Out);
|
||||||
|
|
||||||
var host = new JobHost(config);
|
var host = new JobHost(config);
|
||||||
|
|
|
@ -34,18 +34,116 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Configuration.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Console, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Console.1.1.1\lib\net451\Microsoft.Extensions.Logging.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Primitives, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
|
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Functions.cs" />
|
<Compile Include="Functions.cs" />
|
||||||
|
|
|
@ -1,4 +1,58 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="Microsoft.Extensions.Configuration.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Console" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Primitives" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ComponentModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Http" version="4.3.1" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ObjectModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.CompilerServices.Unsafe" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||||
</packages>
|
</packages>
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings" />
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
|
{
|
||||||
|
internal class LoggerBinding : IBinding
|
||||||
|
{
|
||||||
|
private readonly ParameterInfo _parameter;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
|
public LoggerBinding(ParameterInfo parameter, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_parameter = parameter;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FromAttribute => false;
|
||||||
|
|
||||||
|
public Task<IValueProvider> BindAsync(object value, ValueBindingContext context)
|
||||||
|
{
|
||||||
|
if (value == null || !_parameter.ParameterType.IsAssignableFrom(value.GetType()))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to convert value to {0}.", _parameter.ParameterType));
|
||||||
|
}
|
||||||
|
|
||||||
|
IValueProvider valueProvider = new ValueBinder(value, _parameter.ParameterType);
|
||||||
|
return Task.FromResult<IValueProvider>(valueProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IValueProvider> BindAsync(BindingContext context)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("context");
|
||||||
|
}
|
||||||
|
|
||||||
|
ILogger logger = _loggerFactory.CreateLogger(LogCategories.Function);
|
||||||
|
return BindAsync(logger, context.ValueContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterDescriptor ToParameterDescriptor()
|
||||||
|
{
|
||||||
|
return new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = _parameter.Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ValueBinder : IValueBinder
|
||||||
|
{
|
||||||
|
private readonly object _tracer;
|
||||||
|
private readonly Type _type;
|
||||||
|
|
||||||
|
public ValueBinder(object tracer, Type type)
|
||||||
|
{
|
||||||
|
_tracer = tracer;
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type Type => _type;
|
||||||
|
|
||||||
|
public Task<object> GetValueAsync() => Task.FromResult(_tracer);
|
||||||
|
|
||||||
|
public string ToInvokeString() => null;
|
||||||
|
|
||||||
|
public Task SetValueAsync(object value, CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Binding provider handling bindings to <see cref="ILogger"/>.
|
||||||
|
/// <remarks>
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
internal class LoggerBindingProvider : IBindingProvider
|
||||||
|
{
|
||||||
|
private ILoggerFactory _loggerFactory;
|
||||||
|
public LoggerBindingProvider(ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("context");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterInfo parameter = context.Parameter;
|
||||||
|
if (parameter.ParameterType != typeof(ILogger))
|
||||||
|
{
|
||||||
|
return Task.FromResult<IBinding>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_loggerFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("A parameter of type ILogger cannot be used without a registered ILoggerFactory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
IBinding binding = new LoggerBinding(parameter, _loggerFactory);
|
||||||
|
return Task.FromResult(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,17 +7,21 @@ using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
{
|
{
|
||||||
internal class TraceWriterBinding : IBinding
|
internal class TraceWriterBinding : IBinding
|
||||||
{
|
{
|
||||||
private readonly ParameterInfo _parameter;
|
private readonly ParameterInfo _parameter;
|
||||||
|
private ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
public TraceWriterBinding(ParameterInfo parameter)
|
public TraceWriterBinding(ParameterInfo parameter, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_parameter = parameter;
|
_parameter = parameter;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FromAttribute
|
public bool FromAttribute
|
||||||
|
@ -45,16 +49,24 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
throw new ArgumentNullException("context");
|
throw new ArgumentNullException("context");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TraceWriter trace = context.Trace;
|
||||||
|
|
||||||
|
// If logger functionality is enabled, wrap an ILogger
|
||||||
|
if (_loggerFactory != null)
|
||||||
|
{
|
||||||
|
ILogger logger = _loggerFactory.CreateLogger(LogCategories.Function);
|
||||||
|
trace = new CompositeTraceWriter(new[] { trace, new LoggerTraceWriter(context.Trace.Level, logger) }, null, context.Trace.Level);
|
||||||
|
}
|
||||||
|
|
||||||
object tracer = null;
|
object tracer = null;
|
||||||
if (_parameter.ParameterType == typeof(TraceWriter))
|
if (_parameter.ParameterType == typeof(TraceWriter))
|
||||||
{
|
{
|
||||||
// bind directly to the context TraceWriter
|
tracer = trace;
|
||||||
tracer = context.Trace;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// bind to an adapter
|
// bind to an adapter
|
||||||
tracer = TextWriterTraceAdapter.Synchronized(context.Trace);
|
tracer = TextWriterTraceAdapter.Synchronized(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return BindAsync(tracer, context.ValueContext);
|
return BindAsync(tracer, context.ValueContext);
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
{
|
{
|
||||||
|
@ -15,6 +16,13 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TraceWriterBindingProvider : IBindingProvider
|
internal class TraceWriterBindingProvider : IBindingProvider
|
||||||
{
|
{
|
||||||
|
private ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
|
public TraceWriterBindingProvider(ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
|
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
|
@ -29,7 +37,7 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
|
||||||
return Task.FromResult<IBinding>(null);
|
return Task.FromResult<IBinding>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
IBinding binding = new TraceWriterBinding(parameter);
|
IBinding binding = new TraceWriterBinding(parameter, _loggerFactory);
|
||||||
return Task.FromResult(binding);
|
return Task.FromResult(binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
{
|
{
|
||||||
|
@ -26,6 +27,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly string _functionId;
|
private readonly string _functionId;
|
||||||
private readonly IStorageAccount _hostAccount;
|
private readonly IStorageAccount _hostAccount;
|
||||||
private readonly IStorageAccount _dataAccount;
|
private readonly IStorageAccount _dataAccount;
|
||||||
|
@ -42,6 +44,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
string functionId,
|
string functionId,
|
||||||
IStorageAccount hostAccount,
|
IStorageAccount hostAccount,
|
||||||
IStorageAccount dataAccount,
|
IStorageAccount dataAccount,
|
||||||
|
@ -128,6 +131,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_functionId = functionId;
|
_functionId = functionId;
|
||||||
_hostAccount = hostAccount;
|
_hostAccount = hostAccount;
|
||||||
_dataAccount = dataAccount;
|
_dataAccount = dataAccount;
|
||||||
|
@ -168,7 +172,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
// notification queue and dispatch to the target job function.
|
// notification queue and dispatch to the target job function.
|
||||||
SharedBlobQueueListener sharedBlobQueueListener = _sharedContextProvider.GetOrCreateInstance<SharedBlobQueueListener>(
|
SharedBlobQueueListener sharedBlobQueueListener = _sharedContextProvider.GetOrCreateInstance<SharedBlobQueueListener>(
|
||||||
new SharedBlobQueueListenerFactory(_hostAccount, sharedQueueWatcher, hostBlobTriggerQueue,
|
new SharedBlobQueueListenerFactory(_hostAccount, sharedQueueWatcher, hostBlobTriggerQueue,
|
||||||
_queueConfiguration, _exceptionHandler, _trace, sharedBlobListener.BlobWritterWatcher));
|
_queueConfiguration, _exceptionHandler, _trace, _loggerFactory, sharedBlobListener.BlobWritterWatcher));
|
||||||
var queueListener = new BlobListener(sharedBlobQueueListener);
|
var queueListener = new BlobListener(sharedBlobQueueListener);
|
||||||
|
|
||||||
// determine which client to use for the poison queue
|
// determine which client to use for the poison queue
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Queues.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
private readonly IBlobWrittenWatcher _blobWrittenWatcher;
|
private readonly IBlobWrittenWatcher _blobWrittenWatcher;
|
||||||
private readonly IStorageAccount _hostAccount;
|
private readonly IStorageAccount _hostAccount;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
public SharedBlobQueueListenerFactory(
|
public SharedBlobQueueListenerFactory(
|
||||||
IStorageAccount hostAccount,
|
IStorageAccount hostAccount,
|
||||||
|
@ -32,6 +34,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
IQueueConfiguration queueConfiguration,
|
IQueueConfiguration queueConfiguration,
|
||||||
IWebJobsExceptionHandler exceptionHandler,
|
IWebJobsExceptionHandler exceptionHandler,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
IBlobWrittenWatcher blobWrittenWatcher)
|
IBlobWrittenWatcher blobWrittenWatcher)
|
||||||
{
|
{
|
||||||
if (hostAccount == null)
|
if (hostAccount == null)
|
||||||
|
@ -75,6 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
_queueConfiguration = queueConfiguration;
|
_queueConfiguration = queueConfiguration;
|
||||||
_exceptionHandler = exceptionHandler;
|
_exceptionHandler = exceptionHandler;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_blobWrittenWatcher = blobWrittenWatcher;
|
_blobWrittenWatcher = blobWrittenWatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +97,12 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
|
||||||
|
|
||||||
// this special queue bypasses the QueueProcessorFactory - we don't want people to
|
// this special queue bypasses the QueueProcessorFactory - we don't want people to
|
||||||
// override this
|
// override this
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_hostBlobTriggerQueue.SdkObject, _trace, _queueConfiguration, defaultPoisonQueue.SdkObject);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_hostBlobTriggerQueue.SdkObject, _trace, _loggerFactory,
|
||||||
|
_queueConfiguration, defaultPoisonQueue.SdkObject);
|
||||||
SharedBlobQueueProcessor queueProcessor = new SharedBlobQueueProcessor(context, triggerExecutor);
|
SharedBlobQueueProcessor queueProcessor = new SharedBlobQueueProcessor(context, triggerExecutor);
|
||||||
|
|
||||||
IListener listener = new QueueListener(_hostBlobTriggerQueue, defaultPoisonQueue, triggerExecutor,
|
IListener listener = new QueueListener(_hostBlobTriggerQueue, defaultPoisonQueue, triggerExecutor,
|
||||||
_exceptionHandler, _trace, _sharedQueueWatcher, _queueConfiguration, queueProcessor);
|
_exceptionHandler, _trace, _loggerFactory, _sharedQueueWatcher, _queueConfiguration, queueProcessor);
|
||||||
|
|
||||||
return new SharedBlobQueueListener(listener, triggerExecutor);
|
return new SharedBlobQueueListener(listener, triggerExecutor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Converters;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
|
@ -17,6 +16,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
|
@ -35,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly SingletonManager _singletonManager;
|
private readonly SingletonManager _singletonManager;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
public BlobTriggerAttributeBindingProvider(INameResolver nameResolver,
|
public BlobTriggerAttributeBindingProvider(INameResolver nameResolver,
|
||||||
IStorageAccountProvider accountProvider,
|
IStorageAccountProvider accountProvider,
|
||||||
|
@ -47,7 +48,8 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
SingletonManager singletonManager,
|
SingletonManager singletonManager,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (accountProvider == null)
|
if (accountProvider == null)
|
||||||
{
|
{
|
||||||
|
@ -116,6 +118,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IBlobArgumentBindingProvider CreateProvider(IEnumerable<Type> cloudBlobStreamBinderTypes)
|
private static IBlobArgumentBindingProvider CreateProvider(IEnumerable<Type> cloudBlobStreamBinderTypes)
|
||||||
|
@ -172,7 +175,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
|
|
||||||
ITriggerBinding binding = new BlobTriggerBinding(parameter, argumentBinding, hostAccount, dataAccount, path,
|
ITriggerBinding binding = new BlobTriggerBinding(parameter, argumentBinding, hostAccount, dataAccount, path,
|
||||||
_hostIdProvider, _queueConfiguration, _blobsConfiguration, _exceptionHandler, _blobWrittenWatcherSetter,
|
_hostIdProvider, _queueConfiguration, _blobsConfiguration, _exceptionHandler, _blobWrittenWatcherSetter,
|
||||||
_messageEnqueuedWatcherSetter, _sharedContextProvider, _singletonManager, _trace);
|
_messageEnqueuedWatcherSetter, _sharedContextProvider, _singletonManager, _trace, _loggerFactory);
|
||||||
|
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
|
@ -38,6 +39,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IAsyncObjectToTypeConverter<IStorageBlob> _converter;
|
private readonly IAsyncObjectToTypeConverter<IStorageBlob> _converter;
|
||||||
private readonly IReadOnlyDictionary<string, Type> _bindingDataContract;
|
private readonly IReadOnlyDictionary<string, Type> _bindingDataContract;
|
||||||
private readonly SingletonManager _singletonManager;
|
private readonly SingletonManager _singletonManager;
|
||||||
|
@ -55,7 +57,8 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
SingletonManager singletonManager,
|
SingletonManager singletonManager,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (parameter == null)
|
if (parameter == null)
|
||||||
{
|
{
|
||||||
|
@ -147,6 +150,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_converter = CreateConverter(_blobClient);
|
_converter = CreateConverter(_blobClient);
|
||||||
_bindingDataContract = CreateBindingDataContract(path);
|
_bindingDataContract = CreateBindingDataContract(path);
|
||||||
}
|
}
|
||||||
|
@ -263,9 +267,9 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
|
||||||
|
|
||||||
IStorageBlobContainer container = _blobClient.GetContainerReference(_path.ContainerNamePattern);
|
IStorageBlobContainer container = _blobClient.GetContainerReference(_path.ContainerNamePattern);
|
||||||
|
|
||||||
var factory = new BlobListenerFactory(_hostIdProvider, _queueConfiguration, _blobsConfiguration,
|
var factory = new BlobListenerFactory(_hostIdProvider, _queueConfiguration, _blobsConfiguration, _exceptionHandler,
|
||||||
_exceptionHandler, _blobWrittenWatcherSetter, _messageEnqueuedWatcherSetter,
|
_blobWrittenWatcherSetter, _messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, _loggerFactory,
|
||||||
_sharedContextProvider, _trace, context.Descriptor.Id, _hostAccount, _dataAccount, container, _path, context.Executor, _singletonManager);
|
context.Descriptor.Id, _hostAccount, _dataAccount, container, _path, context.Executor, _singletonManager);
|
||||||
|
|
||||||
return factory.CreateAsync(context.CancellationToken);
|
return factory.CreateAsync(context.CancellationToken);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host
|
namespace Microsoft.Azure.WebJobs.Host
|
||||||
{
|
{
|
||||||
|
@ -71,7 +72,8 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
/// Recovers if exception is handled by trace pipeline, else throws.
|
/// Recovers if exception is handled by trace pipeline, else throws.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="trace"></param>
|
/// <param name="trace"></param>
|
||||||
internal void TryRecover(TraceWriter trace)
|
/// <param name="logger"></param>
|
||||||
|
internal void TryRecover(TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
if (trace == null)
|
if (trace == null)
|
||||||
{
|
{
|
||||||
|
@ -79,6 +81,7 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
trace.Error(Message, this);
|
trace.Error(Message, this);
|
||||||
|
logger?.LogError(0, this, Message);
|
||||||
if (!Handled)
|
if (!Handled)
|
||||||
{
|
{
|
||||||
throw this;
|
throw this;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
|
{
|
||||||
|
internal class CompositeFunctionEventCollector : IAsyncCollector<FunctionInstanceLogEntry>
|
||||||
|
{
|
||||||
|
private IEnumerable<IAsyncCollector<FunctionInstanceLogEntry>> _collectors;
|
||||||
|
|
||||||
|
public CompositeFunctionEventCollector(params IAsyncCollector<FunctionInstanceLogEntry>[] collectors)
|
||||||
|
{
|
||||||
|
_collectors = collectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddAsync(FunctionInstanceLogEntry item, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
foreach (IAsyncCollector<FunctionInstanceLogEntry> collector in _collectors)
|
||||||
|
{
|
||||||
|
await collector.AddAsync(item, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
foreach (IAsyncCollector<FunctionInstanceLogEntry> collector in _collectors)
|
||||||
|
{
|
||||||
|
await collector.FlushAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Executors
|
namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
{
|
{
|
||||||
|
@ -26,12 +27,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
private readonly IFunctionOutputLogger _functionOutputLogger;
|
private readonly IFunctionOutputLogger _functionOutputLogger;
|
||||||
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
private readonly IAsyncCollector<FunctionInstanceLogEntry> _fastLogger;
|
private readonly IAsyncCollector<FunctionInstanceLogEntry> _functionEventCollector;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILogger _resultsLogger;
|
||||||
|
|
||||||
private HostOutputMessage _hostOutputMessage;
|
private HostOutputMessage _hostOutputMessage;
|
||||||
|
|
||||||
public FunctionExecutor(IFunctionInstanceLogger functionInstanceLogger, IFunctionOutputLogger functionOutputLogger,
|
public FunctionExecutor(IFunctionInstanceLogger functionInstanceLogger, IFunctionOutputLogger functionOutputLogger,
|
||||||
IWebJobsExceptionHandler exceptionHandler, TraceWriter trace, IAsyncCollector<FunctionInstanceLogEntry> fastLogger = null)
|
IWebJobsExceptionHandler exceptionHandler, TraceWriter trace, IAsyncCollector<FunctionInstanceLogEntry> functionEventCollector = null,
|
||||||
|
ILoggerFactory loggerFactory = null)
|
||||||
{
|
{
|
||||||
if (functionInstanceLogger == null)
|
if (functionInstanceLogger == null)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +61,9 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
_functionOutputLogger = functionOutputLogger;
|
_functionOutputLogger = functionOutputLogger;
|
||||||
_exceptionHandler = exceptionHandler;
|
_exceptionHandler = exceptionHandler;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
_fastLogger = fastLogger;
|
_functionEventCollector = functionEventCollector;
|
||||||
|
_logger = loggerFactory?.CreateLogger(LogCategories.Executor);
|
||||||
|
_resultsLogger = loggerFactory?.CreateLogger(LogCategories.Results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HostOutputMessage HostOutputMessage
|
public HostOutputMessage HostOutputMessage
|
||||||
|
@ -84,9 +90,13 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
StartTime = functionStartedMessage.StartTime.DateTime
|
StartTime = functionStartedMessage.StartTime.DateTime
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
functionStartedMessageId = await ExecuteWithLoggingAsync(functionInstance, functionStartedMessage, fastItem, parameterLogCollector, functionTraceLevel, cancellationToken);
|
using (_logger?.BeginFunctionScope(functionInstance))
|
||||||
|
{
|
||||||
|
functionStartedMessageId = await ExecuteWithLoggingAsync(functionInstance, functionStartedMessage, fastItem, parameterLogCollector, functionTraceLevel, cancellationToken);
|
||||||
|
}
|
||||||
functionCompletedMessage = CreateCompletedMessage(functionStartedMessage);
|
functionCompletedMessage = CreateCompletedMessage(functionStartedMessage);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
@ -124,12 +134,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
logCompletedCancellationToken = cancellationToken;
|
logCompletedCancellationToken = cancellationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fastLogger != null)
|
// log result
|
||||||
{
|
sw.Stop();
|
||||||
// Log completed
|
fastItem.EndTime = DateTime.UtcNow;
|
||||||
fastItem.EndTime = DateTime.UtcNow;
|
fastItem.Duration = sw.Elapsed;
|
||||||
fastItem.Arguments = functionCompletedMessage.Arguments;
|
fastItem.Arguments = functionCompletedMessage.Arguments;
|
||||||
|
|
||||||
|
if (_functionEventCollector != null)
|
||||||
|
{
|
||||||
|
// Log completed
|
||||||
if (exceptionInfo != null)
|
if (exceptionInfo != null)
|
||||||
{
|
{
|
||||||
var ex = exceptionInfo.SourceException;
|
var ex = exceptionInfo.SourceException;
|
||||||
|
@ -139,7 +152,11 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
}
|
}
|
||||||
fastItem.ErrorDetails = ex.Message;
|
fastItem.ErrorDetails = ex.Message;
|
||||||
}
|
}
|
||||||
await _fastLogger.AddAsync(fastItem);
|
await _functionEventCollector.AddAsync(fastItem);
|
||||||
|
}
|
||||||
|
using (_resultsLogger?.BeginFunctionScope(functionInstance))
|
||||||
|
{
|
||||||
|
_resultsLogger?.LogFunctionResult(functionInstance.FunctionDescriptor.Method.Name, fastItem, sw.Elapsed, exceptionInfo?.SourceException);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionCompletedMessage != null &&
|
if (functionCompletedMessage != null &&
|
||||||
|
@ -229,16 +246,16 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken);
|
startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fastLogger != null)
|
if (_functionEventCollector != null)
|
||||||
{
|
{
|
||||||
// Log started
|
// Log started
|
||||||
fastItem.Arguments = message.Arguments;
|
fastItem.Arguments = message.Arguments;
|
||||||
await _fastLogger.AddAsync(fastItem);
|
await _functionEventCollector.AddAsync(fastItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ExecuteWithLoggingAsync(instance, parameters, traceWriter, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource);
|
await ExecuteWithLoggingAsync(instance, parameters, traceWriter, _logger, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -318,7 +335,8 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
/// create and start the timer.
|
/// create and start the timer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
||||||
internal static System.Timers.Timer StartFunctionTimeout(IFunctionInstance instance, TimeoutAttribute attribute, CancellationTokenSource cancellationTokenSource, TraceWriter trace)
|
internal static System.Timers.Timer StartFunctionTimeout(IFunctionInstance instance, TimeoutAttribute attribute,
|
||||||
|
CancellationTokenSource cancellationTokenSource, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
if (attribute == null)
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
|
@ -348,7 +366,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
|
|
||||||
timer.Elapsed += (o, e) =>
|
timer.Elapsed += (o, e) =>
|
||||||
{
|
{
|
||||||
OnFunctionTimeout(timer, method, instance.Id, timeout.Value, attribute.TimeoutWhileDebugging, trace, cancellationTokenSource,
|
OnFunctionTimeout(timer, method, instance.Id, timeout.Value, attribute.TimeoutWhileDebugging, trace, logger, cancellationTokenSource,
|
||||||
() => Debugger.IsAttached);
|
() => Debugger.IsAttached);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -361,7 +379,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void OnFunctionTimeout(System.Timers.Timer timer, MethodInfo method, Guid instanceId, TimeSpan timeout, bool timeoutWhileDebugging,
|
internal static void OnFunctionTimeout(System.Timers.Timer timer, MethodInfo method, Guid instanceId, TimeSpan timeout, bool timeoutWhileDebugging,
|
||||||
TraceWriter trace, CancellationTokenSource cancellationTokenSource, Func<bool> isDebuggerAttached)
|
TraceWriter trace, ILogger logger, CancellationTokenSource cancellationTokenSource, Func<bool> isDebuggerAttached)
|
||||||
{
|
{
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
|
@ -372,6 +390,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
shouldTimeout ? "Initiating cancellation." : "Function will not be cancelled while debugging.");
|
shouldTimeout ? "Initiating cancellation." : "Function will not be cancelled while debugging.");
|
||||||
|
|
||||||
trace.Error(message, null, TraceSource.Execution);
|
trace.Error(message, null, TraceSource.Execution);
|
||||||
|
logger?.LogError(message);
|
||||||
|
|
||||||
trace.Flush();
|
trace.Flush();
|
||||||
|
|
||||||
|
@ -434,6 +453,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
private async Task ExecuteWithLoggingAsync(IFunctionInstance instance,
|
private async Task ExecuteWithLoggingAsync(IFunctionInstance instance,
|
||||||
IReadOnlyDictionary<string, IValueProvider> parameters,
|
IReadOnlyDictionary<string, IValueProvider> parameters,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILogger logger,
|
||||||
IFunctionOutputDefinition outputDefinition,
|
IFunctionOutputDefinition outputDefinition,
|
||||||
IDictionary<string, ParameterLog> parameterLogCollector,
|
IDictionary<string, ParameterLog> parameterLogCollector,
|
||||||
TraceLevel functionTraceLevel,
|
TraceLevel functionTraceLevel,
|
||||||
|
@ -446,13 +466,13 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
if (functionTraceLevel >= TraceLevel.Info)
|
if (functionTraceLevel >= TraceLevel.Info)
|
||||||
{
|
{
|
||||||
parameterWatchers = CreateParameterWatchers(parameters);
|
parameterWatchers = CreateParameterWatchers(parameters);
|
||||||
IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace);
|
IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace, logger);
|
||||||
updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _exceptionHandler);
|
updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _exceptionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ExecuteWithWatchersAsync(instance, parameters, trace, functionCancellationTokenSource);
|
await ExecuteWithWatchersAsync(instance, parameters, trace, logger, functionCancellationTokenSource);
|
||||||
|
|
||||||
if (updateParameterLogTimer != null)
|
if (updateParameterLogTimer != null)
|
||||||
{
|
{
|
||||||
|
@ -496,6 +516,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
internal static async Task ExecuteWithWatchersAsync(IFunctionInstance instance,
|
internal static async Task ExecuteWithWatchersAsync(IFunctionInstance instance,
|
||||||
IReadOnlyDictionary<string, IValueProvider> parameters,
|
IReadOnlyDictionary<string, IValueProvider> parameters,
|
||||||
TraceWriter traceWriter,
|
TraceWriter traceWriter,
|
||||||
|
ILogger logger,
|
||||||
CancellationTokenSource functionCancellationTokenSource)
|
CancellationTokenSource functionCancellationTokenSource)
|
||||||
{
|
{
|
||||||
IFunctionInvoker invoker = instance.Invoker;
|
IFunctionInvoker invoker = instance.Invoker;
|
||||||
|
@ -526,12 +547,12 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
MethodInfo method = instance.FunctionDescriptor.Method;
|
MethodInfo method = instance.FunctionDescriptor.Method;
|
||||||
TimeoutAttribute timeoutAttribute = instance.FunctionDescriptor.TimeoutAttribute;
|
TimeoutAttribute timeoutAttribute = instance.FunctionDescriptor.TimeoutAttribute;
|
||||||
bool throwOnTimeout = timeoutAttribute == null ? false : timeoutAttribute.ThrowOnTimeout;
|
bool throwOnTimeout = timeoutAttribute == null ? false : timeoutAttribute.ThrowOnTimeout;
|
||||||
var timer = StartFunctionTimeout(instance, timeoutAttribute, timeoutTokenSource, traceWriter);
|
var timer = StartFunctionTimeout(instance, timeoutAttribute, timeoutTokenSource, traceWriter, logger);
|
||||||
TimeSpan timerInterval = timer == null ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(timer.Interval);
|
TimeSpan timerInterval = timer == null ? TimeSpan.MinValue : TimeSpan.FromMilliseconds(timer.Interval);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await InvokeAsync(invoker, invokeParameters, timeoutTokenSource, functionCancellationTokenSource,
|
await InvokeAsync(invoker, invokeParameters, timeoutTokenSource, functionCancellationTokenSource,
|
||||||
throwOnTimeout, timerInterval, instance);
|
throwOnTimeout, timerInterval, instance);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,6 +26,7 @@ using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Tables;
|
using Microsoft.Azure.WebJobs.Host.Tables;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Executors
|
namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
{
|
{
|
||||||
|
@ -34,9 +35,9 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
// Do full initialization (both static and runtime).
|
// Do full initialization (both static and runtime).
|
||||||
// This can be called multiple times on a config.
|
// This can be called multiple times on a config.
|
||||||
public static async Task<JobHostContext> CreateAndLogHostStartedAsync(
|
public static async Task<JobHostContext> CreateAndLogHostStartedAsync(
|
||||||
this JobHostConfiguration config,
|
this JobHostConfiguration config,
|
||||||
JobHost host,
|
JobHost host,
|
||||||
CancellationToken shutdownToken,
|
CancellationToken shutdownToken,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
JobHostContext context = await config.CreateJobHostContextAsync(host, shutdownToken, cancellationToken);
|
JobHostContext context = await config.CreateJobHostContextAsync(host, shutdownToken, cancellationToken);
|
||||||
|
@ -121,25 +122,25 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
|
|
||||||
if (singletonManager == null)
|
if (singletonManager == null)
|
||||||
{
|
{
|
||||||
singletonManager = new SingletonManager(storageAccountProvider, exceptionHandler, config.Singleton, trace, hostIdProvider, services.GetService<INameResolver>());
|
singletonManager = new SingletonManager(storageAccountProvider, exceptionHandler, config.Singleton, trace, config.LoggerFactory, hostIdProvider, services.GetService<INameResolver>());
|
||||||
services.AddService<SingletonManager>(singletonManager);
|
services.AddService<SingletonManager>(singletonManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
IExtensionRegistry extensions = services.GetExtensions();
|
IExtensionRegistry extensions = services.GetExtensions();
|
||||||
ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver,
|
ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver,
|
||||||
storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler,
|
storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler,
|
||||||
messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, singletonManager, trace);
|
messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, singletonManager, trace, config.LoggerFactory);
|
||||||
services.AddService<ITriggerBindingProvider>(triggerBindingProvider);
|
services.AddService<ITriggerBindingProvider>(triggerBindingProvider);
|
||||||
|
|
||||||
if (bindingProvider == null)
|
if (bindingProvider == null)
|
||||||
{
|
{
|
||||||
bindingProvider = DefaultBindingProvider.Create(nameResolver, storageAccountProvider, extensionTypeLocator, blobWrittenWatcherAccessor, extensions);
|
bindingProvider = DefaultBindingProvider.Create(nameResolver, config.LoggerFactory, storageAccountProvider, extensionTypeLocator, blobWrittenWatcherAccessor, extensions);
|
||||||
services.AddService<IBindingProvider>(bindingProvider);
|
services.AddService<IBindingProvider>(bindingProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the full runtime intitialization. This includes static initialization.
|
// Do the full runtime intitialization. This includes static initialization.
|
||||||
// This mainly means:
|
// This mainly means:
|
||||||
// - indexing the functions
|
// - indexing the functions
|
||||||
|
@ -161,12 +162,34 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
INameResolver nameResolver = services.GetService<INameResolver>();
|
INameResolver nameResolver = services.GetService<INameResolver>();
|
||||||
IExtensionRegistry extensions = services.GetExtensions();
|
IExtensionRegistry extensions = services.GetExtensions();
|
||||||
IStorageAccountProvider storageAccountProvider = services.GetService<IStorageAccountProvider>();
|
IStorageAccountProvider storageAccountProvider = services.GetService<IStorageAccountProvider>();
|
||||||
|
ILoggerFactory loggerFactory = services.GetService<ILoggerFactory>();
|
||||||
|
IFunctionResultAggregatorFactory aggregatorFactory = services.GetService<IFunctionResultAggregatorFactory>();
|
||||||
|
IAsyncCollector<FunctionInstanceLogEntry> functionEventCollector = null;
|
||||||
|
|
||||||
|
// Create the aggregator if all the pieces are configured
|
||||||
|
IAsyncCollector<FunctionInstanceLogEntry> aggregator = null;
|
||||||
|
if (loggerFactory != null && aggregatorFactory != null && config.Aggregator.IsEnabled)
|
||||||
|
{
|
||||||
|
aggregator = aggregatorFactory.Create(config.Aggregator.BatchSize, config.Aggregator.FlushTimeout, loggerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
IQueueConfiguration queueConfiguration = services.GetService<IQueueConfiguration>();
|
IQueueConfiguration queueConfiguration = services.GetService<IQueueConfiguration>();
|
||||||
var blobsConfiguration = config.Blobs;
|
var blobsConfiguration = config.Blobs;
|
||||||
|
|
||||||
TraceWriter trace = services.GetService<TraceWriter>();
|
TraceWriter trace = services.GetService<TraceWriter>();
|
||||||
IAsyncCollector<FunctionInstanceLogEntry> fastLogger = services.GetService<IAsyncCollector<FunctionInstanceLogEntry>>();
|
IAsyncCollector<FunctionInstanceLogEntry> registeredFunctionEventCollector = services.GetService<IAsyncCollector<FunctionInstanceLogEntry>>();
|
||||||
|
|
||||||
|
if (registeredFunctionEventCollector != null && aggregator != null)
|
||||||
|
{
|
||||||
|
// If there are both an aggregator and a registered FunctionEventCollector, wrap them in a composite
|
||||||
|
functionEventCollector = new CompositeFunctionEventCollector(new[] { registeredFunctionEventCollector, aggregator });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, take whichever one is null (or use null if both are)
|
||||||
|
functionEventCollector = aggregator ?? registeredFunctionEventCollector;
|
||||||
|
}
|
||||||
|
|
||||||
IWebJobsExceptionHandler exceptionHandler = services.GetService<IWebJobsExceptionHandler>();
|
IWebJobsExceptionHandler exceptionHandler = services.GetService<IWebJobsExceptionHandler>();
|
||||||
|
|
||||||
if (exceptionHandler != null)
|
if (exceptionHandler != null)
|
||||||
|
@ -209,25 +232,25 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken);
|
IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(combinedCancellationToken);
|
||||||
|
|
||||||
IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync(combinedCancellationToken);
|
IHostInstanceLogger hostInstanceLogger = await hostInstanceLoggerProvider.GetAsync(combinedCancellationToken);
|
||||||
IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken);
|
IFunctionInstanceLogger functionInstanceLogger = await functionInstanceLoggerProvider.GetAsync(combinedCancellationToken);
|
||||||
IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken);
|
IFunctionOutputLogger functionOutputLogger = await functionOutputLoggerProvider.GetAsync(combinedCancellationToken);
|
||||||
|
|
||||||
if (functionExecutor == null)
|
if (functionExecutor == null)
|
||||||
{
|
{
|
||||||
functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, fastLogger);
|
functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, functionEventCollector, loggerFactory);
|
||||||
services.AddService(functionExecutor);
|
services.AddService(functionExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionIndexProvider == null)
|
if (functionIndexProvider == null)
|
||||||
{
|
{
|
||||||
functionIndexProvider = new FunctionIndexProvider(services.GetService<ITypeLocator>(), triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager, trace);
|
functionIndexProvider = new FunctionIndexProvider(services.GetService<ITypeLocator>(), triggerBindingProvider, bindingProvider, activator, functionExecutor, extensions, singletonManager, trace, loggerFactory);
|
||||||
|
|
||||||
// Important to set this so that the func we passed to DynamicHostIdProvider can pick it up.
|
// Important to set this so that the func we passed to DynamicHostIdProvider can pick it up.
|
||||||
services.AddService<IFunctionIndexProvider>(functionIndexProvider);
|
services.AddService<IFunctionIndexProvider>(functionIndexProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);
|
IFunctionIndex functions = await functionIndexProvider.GetAsync(combinedCancellationToken);
|
||||||
IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace);
|
IListenerFactory functionsListenerFactory = new HostListenerFactory(functions.ReadAll(), singletonManager, activator, nameResolver, trace, loggerFactory);
|
||||||
|
|
||||||
IFunctionExecutor hostCallExecutor;
|
IFunctionExecutor hostCallExecutor;
|
||||||
IListener listener;
|
IListener listener;
|
||||||
|
@ -257,14 +280,14 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient();
|
IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient();
|
||||||
IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName);
|
IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName);
|
||||||
IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
|
IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
|
||||||
queueConfiguration, exceptionHandler, trace, functions,
|
queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
|
||||||
functionInstanceLogger, functionExecutor);
|
functionInstanceLogger, functionExecutor);
|
||||||
|
|
||||||
Guid hostInstanceId = Guid.NewGuid();
|
Guid hostInstanceId = Guid.NewGuid();
|
||||||
string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
|
string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
|
||||||
IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName);
|
IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName);
|
||||||
IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
|
IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
|
||||||
queueConfiguration, exceptionHandler, trace, functions,
|
queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
|
||||||
functionInstanceLogger, functionExecutor);
|
functionInstanceLogger, functionExecutor);
|
||||||
|
|
||||||
HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
|
HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
|
||||||
|
@ -309,15 +332,22 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
IEnumerable<FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
|
IEnumerable<FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
|
||||||
int descriptorsCount = descriptors.Count();
|
int descriptorsCount = descriptors.Count();
|
||||||
|
|
||||||
|
ILogger startupLogger = loggerFactory?.CreateLogger(LogCategories.Startup);
|
||||||
|
|
||||||
if (config.UsingDevelopmentSettings)
|
if (config.UsingDevelopmentSettings)
|
||||||
{
|
{
|
||||||
trace.Verbose(string.Format("Development settings applied"));
|
string msg = "Development settings applied";
|
||||||
|
trace.Verbose(msg);
|
||||||
|
startupLogger?.LogDebug(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptorsCount == 0)
|
if (descriptorsCount == 0)
|
||||||
{
|
{
|
||||||
trace.Warning(string.Format("No job functions found. Try making your job classes and methods public. {0}",
|
string msg = string.Format("No job functions found. Try making your job classes and methods public. {0}",
|
||||||
Constants.ExtensionInitializationMessage), Host.TraceSource.Indexing);
|
Constants.ExtensionInitializationMessage);
|
||||||
|
|
||||||
|
trace.Warning(msg, Host.TraceSource.Indexing);
|
||||||
|
startupLogger?.LogWarning(msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -328,11 +358,12 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
{
|
{
|
||||||
functionsTrace.AppendLine(descriptor.FullName);
|
functionsTrace.AppendLine(descriptor.FullName);
|
||||||
}
|
}
|
||||||
|
string msg = functionsTrace.ToString();
|
||||||
trace.Info(functionsTrace.ToString(), Host.TraceSource.Indexing);
|
trace.Info(msg, Host.TraceSource.Indexing);
|
||||||
|
startupLogger?.LogInformation(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JobHostContext(functions, hostCallExecutor, listener, trace, fastLogger);
|
return new JobHostContext(functions, hostCallExecutor, listener, trace, functionEventCollector, loggerFactory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Executors
|
namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
{
|
{
|
||||||
|
@ -16,7 +17,8 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
private readonly IFunctionExecutor _executor;
|
private readonly IFunctionExecutor _executor;
|
||||||
private readonly IListener _listener;
|
private readonly IListener _listener;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
private readonly IAsyncCollector<FunctionInstanceLogEntry> _fastLogger; // optional
|
private readonly IAsyncCollector<FunctionInstanceLogEntry> _functionEventCollector; // optional
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
|
@ -24,13 +26,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
IFunctionExecutor executor,
|
IFunctionExecutor executor,
|
||||||
IListener listener,
|
IListener listener,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
IAsyncCollector<FunctionInstanceLogEntry> fastLogger = null)
|
IAsyncCollector<FunctionInstanceLogEntry> functionEventCollector = null,
|
||||||
|
ILoggerFactory loggerFactory = null)
|
||||||
{
|
{
|
||||||
_functionLookup = functionLookup;
|
_functionLookup = functionLookup;
|
||||||
_executor = executor;
|
_executor = executor;
|
||||||
_listener = listener;
|
_listener = listener;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
_fastLogger = fastLogger;
|
_functionEventCollector = functionEventCollector;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TraceWriter Trace
|
public TraceWriter Trace
|
||||||
|
@ -69,12 +73,21 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncCollector<FunctionInstanceLogEntry> FastLogger
|
public IAsyncCollector<FunctionInstanceLogEntry> FunctionEventCollector
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
return _fastLogger;
|
return _functionEventCollector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILoggerFactory LoggerFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ThrowIfDisposed();
|
||||||
|
return _loggerFactory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace System.Collections.Generic
|
||||||
|
{
|
||||||
|
internal static class DictionaryExtensions
|
||||||
|
{
|
||||||
|
public static T GetValueOrDefault<T>(this IDictionary<string, object> dictionary, string key)
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
if (dictionary.TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,4 +68,10 @@
|
||||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetPostResolveHook", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Config.FluentBindingRule`1.#ApplyRules()")]
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetPostResolveHook", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Config.FluentBindingRule`1.#ApplyRules()")]
|
||||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.JobHostQueuesConfiguration.#MaxPollingIntervalInt")]
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.JobHostQueuesConfiguration.#MaxPollingIntervalInt")]
|
||||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.JobHostQueuesConfiguration.#MaxPollingIntervalInt")]
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.JobHostQueuesConfiguration.#MaxPollingIntervalInt")]
|
||||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "AddBindingRule", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Config.ExtensionConfigContext.#AddBindingRule`1()")]
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "AddBindingRule", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Config.ExtensionConfigContext.#AddBindingRule`1()")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Extensions.Logging.AppInsightsLoggerExtensions.#AddAppInsights(Microsoft.Extensions.Logging.ILoggerFactory,System.String,System.Func`3<System.String,Microsoft.Extensions.Logging.LogLevel,System.Boolean>)")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Extensions.Logging.AppInsightsLoggerExtensions.#AddAppInsights(Microsoft.Extensions.Logging.ILoggerFactory,System.String,System.Func`3<System.String,Microsoft.Extensions.Logging.LogLevel,System.Boolean>)")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Extensions.Logging.AppInsightsLoggerExtensions.#AddAppInsights(Microsoft.Extensions.Logging.ILoggerFactory,System.String,System.Func`3<System.String,Microsoft.Extensions.Logging.LogLevel,System.Boolean>)")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Loggers.FunctionResultAggregatorFactory.#Create(System.Int32,System.TimeSpan,Microsoft.Extensions.Logging.ILoggerFactory)")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Extensions.Logging.ApplicationInsightsLoggerExtensions.#AddApplicationInsights(Microsoft.Extensions.Logging.ILoggerFactory,System.String,System.Func`3<System.String,Microsoft.Extensions.Logging.LogLevel,System.Boolean>,Microsoft.Azure.WebJobs.Host.Loggers.ITelemetryClientFactory,Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation.SamplingPercentageEstimatorSettings)")]
|
||||||
|
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Host.Loggers.DefaultTelemetryClientFactory.#InitializeConfiguration(System.String,Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation.SamplingPercentageEstimatorSettings)")]
|
|
@ -15,17 +15,19 @@ using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
using Microsoft.Azure.WebJobs.Host.Queues;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Queues.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Tables;
|
using Microsoft.Azure.WebJobs.Host.Tables;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
{
|
{
|
||||||
internal static class DefaultBindingProvider
|
internal static class DefaultBindingProvider
|
||||||
{
|
{
|
||||||
public static IBindingProvider Create(
|
public static IBindingProvider Create(
|
||||||
INameResolver nameResolver,
|
INameResolver nameResolver,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
IStorageAccountProvider storageAccountProvider,
|
IStorageAccountProvider storageAccountProvider,
|
||||||
IExtensionTypeLocator extensionTypeLocator,
|
IExtensionTypeLocator extensionTypeLocator,
|
||||||
IContextGetter<IBlobWrittenWatcher> blobWrittenWatcherGetter,
|
IContextGetter<IBlobWrittenWatcher> blobWrittenWatcherGetter,
|
||||||
IExtensionRegistry extensions)
|
IExtensionRegistry extensions)
|
||||||
{
|
{
|
||||||
List<IBindingProvider> innerProviders = new List<IBindingProvider>();
|
List<IBindingProvider> innerProviders = new List<IBindingProvider>();
|
||||||
|
|
||||||
|
@ -43,7 +45,9 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
|
|
||||||
// The TraceWriter binder handles all remaining TraceWriter/TextWriter parameters. It must come after the
|
// The TraceWriter binder handles all remaining TraceWriter/TextWriter parameters. It must come after the
|
||||||
// Blob binding provider; otherwise bindings like Do([Blob("a/b")] TextWriter blob) wouldn't work.
|
// Blob binding provider; otherwise bindings like Do([Blob("a/b")] TextWriter blob) wouldn't work.
|
||||||
innerProviders.Add(new TraceWriterBindingProvider());
|
innerProviders.Add(new TraceWriterBindingProvider(loggerFactory));
|
||||||
|
|
||||||
|
innerProviders.Add(new LoggerBindingProvider(loggerFactory));
|
||||||
|
|
||||||
ContextAccessor<IBindingProvider> bindingProviderAccessor = new ContextAccessor<IBindingProvider>();
|
ContextAccessor<IBindingProvider> bindingProviderAccessor = new ContextAccessor<IBindingProvider>();
|
||||||
innerProviders.Add(new RuntimeBindingProvider(bindingProviderAccessor));
|
innerProviders.Add(new RuntimeBindingProvider(bindingProviderAccessor));
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Queues;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Queues.Triggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
{
|
{
|
||||||
|
@ -27,15 +28,16 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
IExtensionRegistry extensions,
|
IExtensionRegistry extensions,
|
||||||
SingletonManager singletonManager,
|
SingletonManager singletonManager,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
List<ITriggerBindingProvider> innerProviders = new List<ITriggerBindingProvider>();
|
List<ITriggerBindingProvider> innerProviders = new List<ITriggerBindingProvider>();
|
||||||
innerProviders.Add(new QueueTriggerAttributeBindingProvider(nameResolver, storageAccountProvider,
|
innerProviders.Add(new QueueTriggerAttributeBindingProvider(nameResolver, storageAccountProvider,
|
||||||
queueConfiguration, exceptionHandler, messageEnqueuedWatcherSetter,
|
queueConfiguration, exceptionHandler, messageEnqueuedWatcherSetter,
|
||||||
sharedContextProvider, trace));
|
sharedContextProvider, trace, loggerFactory));
|
||||||
innerProviders.Add(new BlobTriggerAttributeBindingProvider(nameResolver, storageAccountProvider,
|
innerProviders.Add(new BlobTriggerAttributeBindingProvider(nameResolver, storageAccountProvider, extensionTypeLocator,
|
||||||
extensionTypeLocator, hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler,
|
hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler, blobWrittenWatcherSetter,
|
||||||
blobWrittenWatcherSetter, messageEnqueuedWatcherSetter, sharedContextProvider, singletonManager, trace));
|
messageEnqueuedWatcherSetter, sharedContextProvider, singletonManager, trace, loggerFactory));
|
||||||
|
|
||||||
// add any registered extension binding providers
|
// add any registered extension binding providers
|
||||||
foreach (ITriggerBindingProvider provider in extensions.GetExtensions(typeof(ITriggerBindingProvider)))
|
foreach (ITriggerBindingProvider provider in extensions.GetExtensions(typeof(ITriggerBindingProvider)))
|
||||||
|
|
|
@ -7,8 +7,8 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
private readonly IExtensionRegistry _extensions;
|
private readonly IExtensionRegistry _extensions;
|
||||||
private readonly SingletonManager _singletonManager;
|
private readonly SingletonManager _singletonManager;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
private IFunctionIndex _index;
|
private IFunctionIndex _index;
|
||||||
|
|
||||||
|
@ -32,7 +33,8 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
IFunctionExecutor executor,
|
IFunctionExecutor executor,
|
||||||
IExtensionRegistry extensions,
|
IExtensionRegistry extensions,
|
||||||
SingletonManager singletonManager,
|
SingletonManager singletonManager,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (typeLocator == null)
|
if (typeLocator == null)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +84,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
_extensions = extensions;
|
_extensions = extensions;
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IFunctionIndex> GetAsync(CancellationToken cancellationToken)
|
public async Task<IFunctionIndex> GetAsync(CancellationToken cancellationToken)
|
||||||
|
@ -97,7 +100,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
private async Task<IFunctionIndex> CreateAsync(CancellationToken cancellationToken)
|
private async Task<IFunctionIndex> CreateAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
FunctionIndex index = new FunctionIndex();
|
FunctionIndex index = new FunctionIndex();
|
||||||
FunctionIndexer indexer = new FunctionIndexer(_triggerBindingProvider, _bindingProvider, _activator, _executor, _extensions, _singletonManager, _trace);
|
FunctionIndexer indexer = new FunctionIndexer(_triggerBindingProvider, _bindingProvider, _activator, _executor, _extensions, _singletonManager, _trace, _loggerFactory);
|
||||||
IReadOnlyList<Type> types = _typeLocator.GetTypes();
|
IReadOnlyList<Type> types = _typeLocator.GetTypes();
|
||||||
|
|
||||||
foreach (Type type in types)
|
foreach (Type type in types)
|
||||||
|
|
|
@ -12,8 +12,10 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings.Invoke;
|
using Microsoft.Azure.WebJobs.Host.Bindings.Invoke;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
{
|
{
|
||||||
|
@ -28,8 +30,10 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
private readonly HashSet<Assembly> _jobAttributeAssemblies;
|
private readonly HashSet<Assembly> _jobAttributeAssemblies;
|
||||||
private readonly SingletonManager _singletonManager;
|
private readonly SingletonManager _singletonManager;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public FunctionIndexer(ITriggerBindingProvider triggerBindingProvider, IBindingProvider bindingProvider, IJobActivator activator, IFunctionExecutor executor, IExtensionRegistry extensions, SingletonManager singletonManager, TraceWriter trace)
|
public FunctionIndexer(ITriggerBindingProvider triggerBindingProvider, IBindingProvider bindingProvider, IJobActivator activator, IFunctionExecutor executor, IExtensionRegistry extensions, SingletonManager singletonManager,
|
||||||
|
TraceWriter trace, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (triggerBindingProvider == null)
|
if (triggerBindingProvider == null)
|
||||||
{
|
{
|
||||||
|
@ -73,6 +77,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
_jobAttributeAssemblies = GetJobAttributeAssemblies(extensions);
|
_jobAttributeAssemblies = GetJobAttributeAssemblies(extensions);
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_logger = loggerFactory?.CreateLogger(LogCategories.Startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task IndexTypeAsync(Type type, IFunctionIndexCollector index, CancellationToken cancellationToken)
|
public async Task IndexTypeAsync(Type type, IFunctionIndexCollector index, CancellationToken cancellationToken)
|
||||||
|
@ -85,7 +90,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
}
|
}
|
||||||
catch (FunctionIndexingException fex)
|
catch (FunctionIndexingException fex)
|
||||||
{
|
{
|
||||||
fex.TryRecover(_trace);
|
fex.TryRecover(_trace, _logger);
|
||||||
// If recoverable, continue to the rest of the methods.
|
// If recoverable, continue to the rest of the methods.
|
||||||
// The method in error simply won't be running in the JobHost.
|
// The method in error simply won't be running in the JobHost.
|
||||||
continue;
|
continue;
|
||||||
|
@ -123,7 +128,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
// create a set containing our own core assemblies
|
// create a set containing our own core assemblies
|
||||||
HashSet<Assembly> assemblies = new HashSet<Assembly>();
|
HashSet<Assembly> assemblies = new HashSet<Assembly>();
|
||||||
assemblies.Add(typeof(BlobAttribute).Assembly);
|
assemblies.Add(typeof(BlobAttribute).Assembly);
|
||||||
|
|
||||||
// add any extension assemblies
|
// add any extension assemblies
|
||||||
assemblies.UnionWith(extensions.GetExtensionAssemblies());
|
assemblies.UnionWith(extensions.GetExtensionAssemblies());
|
||||||
|
|
||||||
|
@ -206,7 +211,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
if (triggerBinding != null && !hasNoAutomaticTriggerAttribute)
|
if (triggerBinding != null && !hasNoAutomaticTriggerAttribute)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
string.Format(Constants.UnableToBindParameterFormat,
|
string.Format(Constants.UnableToBindParameterFormat,
|
||||||
parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage));
|
parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -246,7 +251,9 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
|
||||||
|
|
||||||
if (TypeUtility.IsAsyncVoid(method))
|
if (TypeUtility.IsAsyncVoid(method))
|
||||||
{
|
{
|
||||||
this._trace.Warning($"Function '{method.Name}' is async but does not return a Task. Your function may not run correctly.");
|
string msg = $"Function '{method.Name}' is async but does not return a Task. Your function may not run correctly.";
|
||||||
|
_trace.Warning(msg);
|
||||||
|
_logger?.LogWarning(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type returnType = method.ReturnType;
|
Type returnType = method.ReturnType;
|
||||||
|
|
|
@ -12,7 +12,9 @@ using Microsoft.Azure.WebJobs.Host;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs
|
namespace Microsoft.Azure.WebJobs
|
||||||
|
@ -36,7 +38,7 @@ namespace Microsoft.Azure.WebJobs
|
||||||
private Task<JobHostContext> _contextTask;
|
private Task<JobHostContext> _contextTask;
|
||||||
private bool _contextTaskInitialized;
|
private bool _contextTaskInitialized;
|
||||||
private object _contextTaskLock = new object();
|
private object _contextTaskLock = new object();
|
||||||
|
|
||||||
private JobHostContext _context;
|
private JobHostContext _context;
|
||||||
private IListener _listener;
|
private IListener _listener;
|
||||||
private object _contextLock = new object();
|
private object _contextLock = new object();
|
||||||
|
@ -46,6 +48,8 @@ namespace Microsoft.Azure.WebJobs
|
||||||
private object _stopTaskLock = new object();
|
private object _stopTaskLock = new object();
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
|
private ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="JobHost"/> class, using a Microsoft Azure Storage connection
|
/// Initializes a new instance of the <see cref="JobHost"/> class, using a Microsoft Azure Storage connection
|
||||||
/// string located in the connectionStrings section of the configuration file or in environment variables.
|
/// string located in the connectionStrings section of the configuration file or in environment variables.
|
||||||
|
@ -114,7 +118,10 @@ namespace Microsoft.Azure.WebJobs
|
||||||
await EnsureHostStartedAsync(cancellationToken);
|
await EnsureHostStartedAsync(cancellationToken);
|
||||||
|
|
||||||
await _listener.StartAsync(cancellationToken);
|
await _listener.StartAsync(cancellationToken);
|
||||||
_context.Trace.Info("Job host started", Host.TraceSource.Host);
|
|
||||||
|
string msg = "Job host started";
|
||||||
|
_context.Trace.Info(msg, Host.TraceSource.Host);
|
||||||
|
_logger?.LogInformation(msg);
|
||||||
|
|
||||||
_state = StateStarted;
|
_state = StateStarted;
|
||||||
}
|
}
|
||||||
|
@ -156,13 +163,15 @@ namespace Microsoft.Azure.WebJobs
|
||||||
await _listener.StopAsync(cancellationToken);
|
await _listener.StopAsync(cancellationToken);
|
||||||
|
|
||||||
// Flush remaining logs
|
// Flush remaining logs
|
||||||
var fastLogger = _context.FastLogger;
|
var functionEventCollector = _context.FunctionEventCollector;
|
||||||
if (fastLogger != null)
|
if (functionEventCollector != null)
|
||||||
{
|
{
|
||||||
await fastLogger.FlushAsync(cancellationToken);
|
await functionEventCollector.FlushAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Trace.Info("Job host stopped", Host.TraceSource.Host);
|
string msg = "Job host stopped";
|
||||||
|
_context.Trace.Info(msg, Host.TraceSource.Host);
|
||||||
|
_logger?.LogInformation(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Runs the host and blocks the current thread while the host remains running.</summary>
|
/// <summary>Runs the host and blocks the current thread while the host remains running.</summary>
|
||||||
|
@ -321,7 +330,7 @@ namespace Microsoft.Azure.WebJobs
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<JobHostContext> CreateContextAndLogHostStartedAsync(CancellationToken cancellationToken)
|
private async Task<JobHostContext> CreateContextAndLogHostStartedAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
JobHostContext context = await _config.CreateAndLogHostStartedAsync(this, _shutdownTokenSource.Token, cancellationToken);
|
JobHostContext context = await _config.CreateAndLogHostStartedAsync(this, _shutdownTokenSource.Token, cancellationToken);
|
||||||
|
|
||||||
lock (_contextLock)
|
lock (_contextLock)
|
||||||
|
@ -333,6 +342,8 @@ namespace Microsoft.Azure.WebJobs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger = _context.LoggerFactory?.CreateLogger(LogCategories.Startup);
|
||||||
|
|
||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
using Microsoft.Azure.WebJobs.Host.Queues;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs
|
namespace Microsoft.Azure.WebJobs
|
||||||
|
@ -43,7 +44,7 @@ namespace Microsoft.Azure.WebJobs
|
||||||
: this(null)
|
: this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="JobHostConfiguration"/> class, using the
|
/// Initializes a new instance of the <see cref="JobHostConfiguration"/> class, using the
|
||||||
/// specified connection string for both reading and writing data as well as Dashboard logging.
|
/// specified connection string for both reading and writing data as well as Dashboard logging.
|
||||||
|
@ -62,6 +63,7 @@ namespace Microsoft.Azure.WebJobs
|
||||||
}
|
}
|
||||||
|
|
||||||
Singleton = new SingletonConfiguration();
|
Singleton = new SingletonConfiguration();
|
||||||
|
Aggregator = new FunctionResultAggregatorConfiguration();
|
||||||
|
|
||||||
// add our built in services here
|
// add our built in services here
|
||||||
_tooling = new JobHostMetadataProvider(this);
|
_tooling = new JobHostMetadataProvider(this);
|
||||||
|
@ -80,9 +82,10 @@ namespace Microsoft.Azure.WebJobs
|
||||||
AddService<ITypeLocator>(typeLocator);
|
AddService<ITypeLocator>(typeLocator);
|
||||||
AddService<IConverterManager>(converterManager);
|
AddService<IConverterManager>(converterManager);
|
||||||
AddService<IWebJobsExceptionHandler>(exceptionHandler);
|
AddService<IWebJobsExceptionHandler>(exceptionHandler);
|
||||||
|
AddService<IFunctionResultAggregatorFactory>(new FunctionResultAggregatorFactory());
|
||||||
|
|
||||||
string value = ConfigurationUtility.GetSettingFromConfigOrEnvironment(Constants.EnvironmentSettingName);
|
string value = ConfigurationUtility.GetSettingFromConfigOrEnvironment(Host.Constants.EnvironmentSettingName);
|
||||||
IsDevelopment = string.Compare(Constants.DevelopmentEnvironmentValue, value, StringComparison.OrdinalIgnoreCase) == 0;
|
IsDevelopment = string.Compare(Host.Constants.DevelopmentEnvironmentValue, value, StringComparison.OrdinalIgnoreCase) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -257,6 +260,31 @@ namespace Microsoft.Azure.WebJobs
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the configuration used by the logging aggregator.
|
||||||
|
/// </summary>
|
||||||
|
public FunctionResultAggregatorConfiguration Aggregator
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ILoggerFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
[CLSCompliant(false)]
|
||||||
|
public ILoggerFactory LoggerFactory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return GetService<ILoggerFactory>();
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
AddService<ILoggerFactory>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the configuration for event tracing.
|
/// Gets the configuration for event tracing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -353,10 +381,10 @@ namespace Microsoft.Azure.WebJobs
|
||||||
}
|
}
|
||||||
|
|
||||||
_services.AddOrUpdate(serviceType, serviceInstance, (key, existingValue) =>
|
_services.AddOrUpdate(serviceType, serviceInstance, (key, existingValue) =>
|
||||||
{
|
{
|
||||||
// always replace existing values
|
// always replace existing values
|
||||||
return serviceInstance;
|
return serviceInstance;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -383,7 +411,7 @@ namespace Microsoft.Azure.WebJobs
|
||||||
{
|
{
|
||||||
_tooling.AddAttributesFromAssembly(assembly);
|
_tooling.AddAttributesFromAssembly(assembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a tooling interface for inspecting current extensions.
|
/// Get a tooling interface for inspecting current extensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -418,4 +446,4 @@ namespace Microsoft.Azure.WebJobs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
{
|
{
|
||||||
|
@ -13,6 +15,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
private readonly IListener _listener;
|
private readonly IListener _listener;
|
||||||
private readonly FunctionDescriptor _descriptor;
|
private readonly FunctionDescriptor _descriptor;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private bool _started = false;
|
private bool _started = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -22,11 +25,13 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
/// <param name="listener"></param>
|
/// <param name="listener"></param>
|
||||||
/// <param name="descriptor"></param>
|
/// <param name="descriptor"></param>
|
||||||
/// <param name="trace"></param>
|
/// <param name="trace"></param>
|
||||||
public FunctionListener(IListener listener, FunctionDescriptor descriptor, TraceWriter trace)
|
/// <param name="loggerFactory"></param>
|
||||||
|
public FunctionListener(IListener listener, FunctionDescriptor descriptor, TraceWriter trace, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_listener = listener;
|
_listener = listener;
|
||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_logger = loggerFactory?.CreateLogger(LogCategories.Startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
public void Cancel()
|
||||||
|
@ -48,10 +53,10 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
new FunctionListenerException(_descriptor.ShortName, e).TryRecover(_trace);
|
new FunctionListenerException(_descriptor.ShortName, e).TryRecover(_trace, _logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_started)
|
if (_started)
|
||||||
|
|
|
@ -10,6 +10,8 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
|
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
{
|
{
|
||||||
|
@ -22,14 +24,19 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
private readonly IJobActivator _activator;
|
private readonly IJobActivator _activator;
|
||||||
private readonly INameResolver _nameResolver;
|
private readonly INameResolver _nameResolver;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public HostListenerFactory(IEnumerable<IFunctionDefinition> functionDefinitions, SingletonManager singletonManager, IJobActivator activator, INameResolver nameResolver, TraceWriter trace)
|
public HostListenerFactory(IEnumerable<IFunctionDefinition> functionDefinitions, SingletonManager singletonManager, IJobActivator activator, INameResolver nameResolver,
|
||||||
|
TraceWriter trace, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_functionDefinitions = functionDefinitions;
|
_functionDefinitions = functionDefinitions;
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
_activator = activator;
|
_activator = activator;
|
||||||
_nameResolver = nameResolver;
|
_nameResolver = nameResolver;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_logger = _loggerFactory?.CreateLogger(LogCategories.Startup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IListener> CreateAsync(CancellationToken cancellationToken)
|
public async Task<IListener> CreateAsync(CancellationToken cancellationToken)
|
||||||
|
@ -48,7 +55,9 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
MethodInfo method = functionDefinition.Descriptor.Method;
|
MethodInfo method = functionDefinition.Descriptor.Method;
|
||||||
if (IsDisabled(method, _nameResolver, _activator))
|
if (IsDisabled(method, _nameResolver, _activator))
|
||||||
{
|
{
|
||||||
_trace.Info(string.Format("Function '{0}' is disabled", functionDefinition.Descriptor.ShortName), TraceSource.Host);
|
string msg = string.Format("Function '{0}' is disabled", functionDefinition.Descriptor.ShortName);
|
||||||
|
_trace.Info(msg, TraceSource.Host);
|
||||||
|
_logger?.LogInformation(msg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +67,11 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
SingletonAttribute singletonAttribute = SingletonManager.GetListenerSingletonOrNull(listener.GetType(), method);
|
SingletonAttribute singletonAttribute = SingletonManager.GetListenerSingletonOrNull(listener.GetType(), method);
|
||||||
if (singletonAttribute != null)
|
if (singletonAttribute != null)
|
||||||
{
|
{
|
||||||
listener = new SingletonListener(method, singletonAttribute, _singletonManager, listener, _trace);
|
listener = new SingletonListener(method, singletonAttribute, _singletonManager, listener, _trace, _loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap the listener with a function listener to handle exceptions
|
// wrap the listener with a function listener to handle exceptions
|
||||||
listener = new FunctionListener(listener, functionDefinition.Descriptor, _trace);
|
listener = new FunctionListener(listener, functionDefinition.Descriptor, _trace, _loggerFactory);
|
||||||
listeners.Add(listener);
|
listeners.Add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +143,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
|
|
||||||
if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
|
if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
|
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
|
||||||
"Type '{0}' must declare a method 'IsDisabled' returning bool and taking a single parameter of Type MethodInfo.", providerType.Name));
|
"Type '{0}' must declare a method 'IsDisabled' returning bool and taking a single parameter of Type MethodInfo.", providerType.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
@ -46,9 +47,9 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
return UpdateOutputLogCommand.Create(blob);
|
return UpdateOutputLogCommand.Create(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace)
|
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
return new UpdateParameterLogCommand(watches, GetBlockBlobReference(_parameterLogBlob), trace);
|
return new UpdateParameterLogCommand(watches, GetBlockBlobReference(_parameterLogBlob), trace, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IStorageBlockBlob GetBlockBlobReference(LocalBlobDescriptor descriptor)
|
private IStorageBlockBlob GetBlockBlobReference(LocalBlobDescriptor descriptor)
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
{
|
{
|
||||||
|
@ -31,7 +32,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
return new ConsoleFunctionOutputLog();
|
return new ConsoleFunctionOutputLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace)
|
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
{
|
{
|
||||||
|
@ -102,7 +103,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace)
|
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
{
|
{
|
||||||
|
@ -41,6 +38,11 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
/// <summary>An optional value for when the function finished executing. If not set, then the function hasn't completed yet. </summary>
|
/// <summary>An optional value for when the function finished executing. If not set, then the function hasn't completed yet. </summary>
|
||||||
public DateTime? EndTime { get; set; }
|
public DateTime? EndTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The duration of the function execution.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Null on success.
|
/// Null on success.
|
||||||
/// Else, set to some string with error details.
|
/// Else, set to some string with error details.
|
||||||
|
@ -48,7 +50,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
public string ErrorDetails { get; set; }
|
public string ErrorDetails { get; set; }
|
||||||
|
|
||||||
/// <summary>Gets or sets the function's argument values and help strings.</summary>
|
/// <summary>Gets or sets the function's argument values and help strings.</summary>
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
|
||||||
public IDictionary<string, string> Arguments { get; set; }
|
public IDictionary<string, string> Arguments { get; set; }
|
||||||
|
|
||||||
/// <summary>Direct inline capture for output written to the per-function instance TextWriter log.
|
/// <summary>Direct inline capture for output written to the per-function instance TextWriter log.
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,6 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
|
||||||
IFunctionOutput CreateOutput();
|
IFunctionOutput CreateOutput();
|
||||||
|
|
||||||
IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace);
|
IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class FunctionResultAggregate
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public DateTimeOffset Timestamp { get; set; }
|
||||||
|
public TimeSpan AverageDuration { get; set; }
|
||||||
|
public TimeSpan MaxDuration { get; set; }
|
||||||
|
public TimeSpan MinDuration { get; set; }
|
||||||
|
public int Successes { get; set; }
|
||||||
|
public int Failures { get; set; }
|
||||||
|
public int Count => Successes + Failures;
|
||||||
|
public double SuccessRate => Math.Round((Successes / (double)Count) * 100, 2);
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, object> ToReadOnlyDictionary()
|
||||||
|
{
|
||||||
|
return new ReadOnlyDictionary<string, object>(new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
[LoggingKeys.Name] = Name,
|
||||||
|
[LoggingKeys.Count] = Count,
|
||||||
|
[LoggingKeys.Timestamp] = Timestamp,
|
||||||
|
[LoggingKeys.AvgDuration] = AverageDuration,
|
||||||
|
[LoggingKeys.MaxDuration] = MaxDuration,
|
||||||
|
[LoggingKeys.MinDuration] = MinDuration,
|
||||||
|
[LoggingKeys.Successes] = Successes,
|
||||||
|
[LoggingKeys.Failures] = Failures,
|
||||||
|
[LoggingKeys.SuccessRate] = SuccessRate
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading.Tasks.Dataflow;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class FunctionResultAggregator : IAsyncCollector<FunctionInstanceLogEntry>, IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly int _batchSize;
|
||||||
|
private readonly TimeSpan _batchTimeout;
|
||||||
|
|
||||||
|
private const string FunctionNamePrefix = "Functions.";
|
||||||
|
|
||||||
|
private BufferBlock<FunctionInstanceLogEntry> _buffer;
|
||||||
|
private BatchBlock<FunctionInstanceLogEntry> _batcher;
|
||||||
|
|
||||||
|
private Timer _windowTimer;
|
||||||
|
private IDisposable[] _disposables;
|
||||||
|
|
||||||
|
public FunctionResultAggregator(int batchSize, TimeSpan batchTimeout, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
if (loggerFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(loggerFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger = loggerFactory.CreateLogger(LogCategories.Aggregator);
|
||||||
|
_batchSize = batchSize;
|
||||||
|
_batchTimeout = batchTimeout;
|
||||||
|
InitializeFlow(_batchSize, _batchTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFlow(int maxBacklog, TimeSpan maxFlushInterval)
|
||||||
|
{
|
||||||
|
_buffer = new BufferBlock<FunctionInstanceLogEntry>(
|
||||||
|
new ExecutionDataflowBlockOptions()
|
||||||
|
{
|
||||||
|
BoundedCapacity = maxBacklog
|
||||||
|
});
|
||||||
|
|
||||||
|
_batcher = new BatchBlock<FunctionInstanceLogEntry>(maxBacklog,
|
||||||
|
new GroupingDataflowBlockOptions()
|
||||||
|
{
|
||||||
|
BoundedCapacity = maxBacklog,
|
||||||
|
Greedy = true
|
||||||
|
});
|
||||||
|
|
||||||
|
TransformBlock<IEnumerable<FunctionInstanceLogEntry>, IEnumerable<FunctionResultAggregate>> aggregator =
|
||||||
|
new TransformBlock<IEnumerable<FunctionInstanceLogEntry>, IEnumerable<FunctionResultAggregate>>(transform: (e) => Aggregate(e));
|
||||||
|
|
||||||
|
ActionBlock<IEnumerable<FunctionResultAggregate>> publisher = new ActionBlock<IEnumerable<FunctionResultAggregate>>(
|
||||||
|
(e) => Publish(e),
|
||||||
|
new ExecutionDataflowBlockOptions()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = 1,
|
||||||
|
BoundedCapacity = 32
|
||||||
|
});
|
||||||
|
|
||||||
|
_disposables = new IDisposable[]
|
||||||
|
{
|
||||||
|
_buffer.LinkTo(_batcher),
|
||||||
|
_batcher.LinkTo(aggregator),
|
||||||
|
aggregator.LinkTo(publisher)
|
||||||
|
};
|
||||||
|
|
||||||
|
_windowTimer = new Timer(async (o) => await FlushAsync(), null, maxFlushInterval, maxFlushInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
_batcher?.TriggerBatch();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Publish(IEnumerable<FunctionResultAggregate> results)
|
||||||
|
{
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
_logger.LogFunctionResultAggregate(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddAsync(FunctionInstanceLogEntry result, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
// We'll get 'Function started' events here, which we don't care about.
|
||||||
|
if (result.EndTime != null)
|
||||||
|
{
|
||||||
|
await _buffer.SendAsync(result, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IEnumerable<FunctionResultAggregate> Aggregate(IEnumerable<FunctionInstanceLogEntry> evts)
|
||||||
|
{
|
||||||
|
var metrics = evts
|
||||||
|
.GroupBy(e => new { e.FunctionName })
|
||||||
|
.Select(e => new FunctionResultAggregate
|
||||||
|
{
|
||||||
|
Name = TrimDefaultClassName(e.Key.FunctionName),
|
||||||
|
Timestamp = e.Min(t => t.StartTime),
|
||||||
|
Successes = e.Count(f => f.ErrorDetails == null),
|
||||||
|
Failures = e.Count(f => f.ErrorDetails != null),
|
||||||
|
MinDuration = e.Min(f => f.Duration),
|
||||||
|
MaxDuration = e.Max(f => f.Duration),
|
||||||
|
AverageDuration = new TimeSpan((long)e.Average(f => f.Duration.Ticks))
|
||||||
|
});
|
||||||
|
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrimDefaultClassName(string functionName)
|
||||||
|
{
|
||||||
|
// We'll strip off the default namespace for nicer logs. Non-default namespaces will remain.
|
||||||
|
if (functionName != null && functionName.StartsWith(FunctionNamePrefix, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
functionName = functionName.Substring(FunctionNamePrefix.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposables != null)
|
||||||
|
{
|
||||||
|
foreach (var d in _disposables)
|
||||||
|
{
|
||||||
|
d.Dispose();
|
||||||
|
}
|
||||||
|
_disposables = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_windowTimer != null)
|
||||||
|
{
|
||||||
|
_windowTimer.Dispose();
|
||||||
|
_windowTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration options for function result aggregation.
|
||||||
|
/// </summary>
|
||||||
|
public class FunctionResultAggregatorConfiguration
|
||||||
|
{
|
||||||
|
private int _batchSize;
|
||||||
|
private TimeSpan _flushTimeout;
|
||||||
|
|
||||||
|
private const int DefaultBatchSize = 1000;
|
||||||
|
private const int MaxBatchSize = 10000;
|
||||||
|
|
||||||
|
private static readonly TimeSpan DefaultFlushTimeout = TimeSpan.FromSeconds(30);
|
||||||
|
private static readonly TimeSpan MaxFlushTimeout = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance.
|
||||||
|
/// </summary>
|
||||||
|
public FunctionResultAggregatorConfiguration()
|
||||||
|
{
|
||||||
|
_batchSize = DefaultBatchSize;
|
||||||
|
_flushTimeout = DefaultFlushTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the aggregator is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating the the maximum batch size for aggregations. When this number is hit, the results are
|
||||||
|
/// aggregated and sent to every registered <see cref="ILogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int BatchSize
|
||||||
|
{
|
||||||
|
get { return _batchSize; }
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > MaxBatchSize)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_batchSize = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating when the aggregator will send results to every registered <see cref="ILogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan FlushTimeout
|
||||||
|
{
|
||||||
|
get { return _flushTimeout; }
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > MaxFlushTimeout)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_flushTimeout = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class FunctionResultAggregatorFactory : IFunctionResultAggregatorFactory
|
||||||
|
{
|
||||||
|
public IAsyncCollector<FunctionInstanceLogEntry> Create(int batchSize, TimeSpan batchTimeout, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
return new FunctionResultAggregator(batchSize, batchTimeout, loggerFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal interface IFunctionResultAggregatorFactory
|
||||||
|
{
|
||||||
|
IAsyncCollector<FunctionInstanceLogEntry> Create(int batchSize, TimeSpan batchTimeout, ILoggerFactory loggerFactory);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Web;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class ApplicationInsightsLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly TelemetryClient _telemetryClient;
|
||||||
|
private readonly string _categoryName;
|
||||||
|
private const string DefaultCategoryName = "Default";
|
||||||
|
private const string DateTimeFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK";
|
||||||
|
private Func<string, LogLevel, bool> _filter;
|
||||||
|
|
||||||
|
public ApplicationInsightsLogger(TelemetryClient client, string categoryName, Func<string, LogLevel, bool> filter)
|
||||||
|
{
|
||||||
|
_telemetryClient = client;
|
||||||
|
_categoryName = categoryName ?? DefaultCategoryName;
|
||||||
|
_filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
|
||||||
|
Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
if (!IsEnabled(logLevel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string formattedMessage = formatter?.Invoke(state, exception);
|
||||||
|
|
||||||
|
IEnumerable<KeyValuePair<string, object>> stateValues = state as IEnumerable<KeyValuePair<string, object>>;
|
||||||
|
|
||||||
|
// We only support lists of key-value pairs. Anything else we'll skip.
|
||||||
|
if (stateValues == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a function result
|
||||||
|
if (_categoryName == LogCategories.Results)
|
||||||
|
{
|
||||||
|
LogFunctionResult(stateValues, exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log an aggregate record
|
||||||
|
if (_categoryName == LogCategories.Aggregator)
|
||||||
|
{
|
||||||
|
LogFunctionResultAggregate(stateValues);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log an exception
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
LogException(logLevel, stateValues, exception, formattedMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, log a trace
|
||||||
|
LogTrace(logLevel, stateValues, formattedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogException(LogLevel logLevel, IEnumerable<KeyValuePair<string, object>> values, Exception exception, string formattedMessage)
|
||||||
|
{
|
||||||
|
ExceptionTelemetry telemetry = new ExceptionTelemetry(exception);
|
||||||
|
telemetry.Message = formattedMessage;
|
||||||
|
telemetry.SeverityLevel = GetSeverityLevel(logLevel);
|
||||||
|
telemetry.Timestamp = DateTimeOffset.UtcNow;
|
||||||
|
ApplyCustomProperties(telemetry, values);
|
||||||
|
_telemetryClient.TrackException(telemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogTrace(LogLevel logLevel, IEnumerable<KeyValuePair<string, object>> values, string formattedMessage)
|
||||||
|
{
|
||||||
|
TraceTelemetry telemetry = new TraceTelemetry(formattedMessage);
|
||||||
|
telemetry.SeverityLevel = GetSeverityLevel(logLevel);
|
||||||
|
telemetry.Timestamp = DateTimeOffset.UtcNow;
|
||||||
|
ApplyCustomProperties(telemetry, values);
|
||||||
|
_telemetryClient.TrackTrace(telemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SeverityLevel? GetSeverityLevel(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
switch (logLevel)
|
||||||
|
{
|
||||||
|
case LogLevel.Trace:
|
||||||
|
case LogLevel.Debug:
|
||||||
|
return SeverityLevel.Verbose;
|
||||||
|
case LogLevel.Information:
|
||||||
|
return SeverityLevel.Information;
|
||||||
|
case LogLevel.Warning:
|
||||||
|
return SeverityLevel.Warning;
|
||||||
|
case LogLevel.Error:
|
||||||
|
return SeverityLevel.Error;
|
||||||
|
case LogLevel.Critical:
|
||||||
|
return SeverityLevel.Critical;
|
||||||
|
case LogLevel.None:
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyCustomProperties(ISupportProperties telemetry, IEnumerable<KeyValuePair<string, object>> values)
|
||||||
|
{
|
||||||
|
telemetry.Properties.Add(LoggingKeys.CategoryName, _categoryName);
|
||||||
|
|
||||||
|
foreach (var property in values)
|
||||||
|
{
|
||||||
|
string stringValue = null;
|
||||||
|
|
||||||
|
// drop null properties
|
||||||
|
if (property.Value == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format dates
|
||||||
|
Type propertyType = property.Value.GetType();
|
||||||
|
if (propertyType == typeof(DateTime))
|
||||||
|
{
|
||||||
|
stringValue = ((DateTime)property.Value).ToUniversalTime().ToString(DateTimeFormatString);
|
||||||
|
}
|
||||||
|
else if (propertyType == typeof(DateTimeOffset))
|
||||||
|
{
|
||||||
|
stringValue = ((DateTimeOffset)property.Value).UtcDateTime.ToString(DateTimeFormatString);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringValue = property.Value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since there is no nesting of properties, apply a prefix before the property name to lessen
|
||||||
|
// the chance of collisions.
|
||||||
|
telemetry.Properties.Add(LoggingKeys.CustomPropertyPrefix + property.Key, stringValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogFunctionResultAggregate(IEnumerable<KeyValuePair<string, object>> values)
|
||||||
|
{
|
||||||
|
// Metric names will be created like "{FunctionName} {MetricName}"
|
||||||
|
IDictionary<string, double> metrics = new Dictionary<string, double>();
|
||||||
|
string functionName = LoggingConstants.Unknown;
|
||||||
|
|
||||||
|
// build up the collection of metrics to send
|
||||||
|
foreach (KeyValuePair<string, object> value in values)
|
||||||
|
{
|
||||||
|
switch (value.Key)
|
||||||
|
{
|
||||||
|
case LoggingKeys.Name:
|
||||||
|
functionName = value.Value.ToString();
|
||||||
|
break;
|
||||||
|
case LoggingKeys.Timestamp:
|
||||||
|
case LoggingKeys.OriginalFormat:
|
||||||
|
// Timestamp is created automatically
|
||||||
|
// We won't use the format string here
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (value.Value is TimeSpan)
|
||||||
|
{
|
||||||
|
// if it's a TimeSpan, log the milliseconds
|
||||||
|
metrics.Add(value.Key, ((TimeSpan)value.Value).TotalMilliseconds);
|
||||||
|
}
|
||||||
|
else if (value.Value is double || value.Value is int)
|
||||||
|
{
|
||||||
|
metrics.Add(value.Key, Convert.ToDouble(value.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// do nothing otherwise
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, double> metric in metrics)
|
||||||
|
{
|
||||||
|
_telemetryClient.TrackMetric($"{functionName} {metric.Key}", metric.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogFunctionResult(IEnumerable<KeyValuePair<string, object>> values, Exception exception)
|
||||||
|
{
|
||||||
|
IDictionary<string, object> scopeProps = DictionaryLoggerScope.GetMergedStateDictionary() ?? new Dictionary<string, object>();
|
||||||
|
|
||||||
|
RequestTelemetry requestTelemetry = new RequestTelemetry();
|
||||||
|
requestTelemetry.Success = exception == null;
|
||||||
|
requestTelemetry.ResponseCode = "0";
|
||||||
|
|
||||||
|
// Set ip address to zeroes. If we find HttpRequest details below, we will update this
|
||||||
|
requestTelemetry.Context.Location.Ip = LoggingConstants.ZeroIpAddress;
|
||||||
|
|
||||||
|
ApplyFunctionResultProperties(requestTelemetry, values);
|
||||||
|
|
||||||
|
// Functions attaches the HttpRequest, which allows us to log richer request details.
|
||||||
|
object request;
|
||||||
|
if (scopeProps.TryGetValue(ScopeKeys.HttpRequest, out request))
|
||||||
|
{
|
||||||
|
ApplyHttpRequestProperties(requestTelemetry, request as HttpRequestMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log associated exception details
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
ExceptionTelemetry exceptionTelemetry = new ExceptionTelemetry(exception);
|
||||||
|
_telemetryClient.TrackException(exceptionTelemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
_telemetryClient.TrackRequest(requestTelemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyHttpRequestProperties(RequestTelemetry requestTelemetry, HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestTelemetry.Url = new Uri(request.RequestUri.GetLeftPart(UriPartial.Path));
|
||||||
|
requestTelemetry.Properties[LoggingKeys.HttpMethod] = request.Method.ToString();
|
||||||
|
|
||||||
|
requestTelemetry.Context.Location.Ip = GetIpAddress(request);
|
||||||
|
requestTelemetry.Context.User.UserAgent = request.Headers.UserAgent?.ToString();
|
||||||
|
|
||||||
|
HttpResponseMessage response = GetResponse(request);
|
||||||
|
|
||||||
|
// If a function throws an exception, we don't get a response attached to the request.
|
||||||
|
// In that case, we'll consider it a 500.
|
||||||
|
if (response?.StatusCode != null)
|
||||||
|
{
|
||||||
|
requestTelemetry.ResponseCode = ((int)response.StatusCode).ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestTelemetry.ResponseCode = "500";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyFunctionResultProperties(RequestTelemetry requestTelemetry, IEnumerable<KeyValuePair<string, object>> stateValues)
|
||||||
|
{
|
||||||
|
// Build up the telemetry model. Some values special and go right on the telemetry object. All others
|
||||||
|
// are added to the Properties bag.
|
||||||
|
foreach (KeyValuePair<string, object> prop in stateValues)
|
||||||
|
{
|
||||||
|
switch (prop.Key)
|
||||||
|
{
|
||||||
|
case LoggingKeys.Name:
|
||||||
|
requestTelemetry.Name = prop.Value.ToString();
|
||||||
|
break;
|
||||||
|
case LoggingKeys.InvocationId:
|
||||||
|
requestTelemetry.Id = prop.Value.ToString();
|
||||||
|
break;
|
||||||
|
case LoggingKeys.StartTime:
|
||||||
|
DateTimeOffset startTime = new DateTimeOffset((DateTime)prop.Value, TimeSpan.Zero);
|
||||||
|
requestTelemetry.Timestamp = startTime;
|
||||||
|
requestTelemetry.Properties.Add(prop.Key, startTime.ToString(DateTimeFormatString));
|
||||||
|
break;
|
||||||
|
case LoggingKeys.Duration:
|
||||||
|
if (prop.Value is TimeSpan)
|
||||||
|
{
|
||||||
|
requestTelemetry.Duration = (TimeSpan)prop.Value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LoggingKeys.OriginalFormat:
|
||||||
|
// this is the format string; we won't use it here
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (prop.Value is DateTime)
|
||||||
|
{
|
||||||
|
DateTimeOffset date = new DateTimeOffset((DateTime)prop.Value, TimeSpan.Zero);
|
||||||
|
requestTelemetry.Properties.Add(prop.Key, date.ToString(DateTimeFormatString));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestTelemetry.Properties.Add(prop.Key, prop.Value?.ToString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
if (_filter == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _filter(_categoryName, logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
{
|
||||||
|
if (state == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DictionaryLoggerScope.Push(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetIpAddress(HttpRequestMessage httpRequest)
|
||||||
|
{
|
||||||
|
// first check for X-Forwarded-For; used by load balancers
|
||||||
|
IEnumerable<string> headers;
|
||||||
|
if (httpRequest.Headers.TryGetValues(ScopeKeys.ForwardedForHeaderName, out headers))
|
||||||
|
{
|
||||||
|
string ip = headers.FirstOrDefault();
|
||||||
|
if (!string.IsNullOrWhiteSpace(ip))
|
||||||
|
{
|
||||||
|
return RemovePort(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpContextBase context = httpRequest.Properties.GetValueOrDefault<HttpContextBase>(ScopeKeys.HttpContext);
|
||||||
|
return context?.Request?.UserHostAddress ?? LoggingConstants.ZeroIpAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RemovePort(string address)
|
||||||
|
{
|
||||||
|
// For Web sites in Azure header contains ip address with port e.g. 50.47.87.223:54464
|
||||||
|
int portSeparatorIndex = address.IndexOf(":", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (portSeparatorIndex > 0)
|
||||||
|
{
|
||||||
|
return address.Substring(0, portSeparatorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static HttpResponseMessage GetResponse(HttpRequestMessage httpRequest)
|
||||||
|
{
|
||||||
|
// Grab the response stored by functions
|
||||||
|
return httpRequest.Properties.GetValueOrDefault<HttpResponseMessage>(ScopeKeys.FunctionsHttpResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Logging
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions for adding the <see cref="ApplicationInsightsLoggerProvider"/> to an <see cref="ILoggerFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
[CLSCompliant(false)]
|
||||||
|
public static class ApplicationInsightsLoggerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="ILoggerFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loggerFactory">The factory.</param>
|
||||||
|
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
|
||||||
|
/// <param name="filter">A filter that returns true if a message with the specified <see cref="LogLevel"/>
|
||||||
|
/// and category should be logged. You can use <see cref="LogCategoryFilter.Filter(string, LogLevel)"/>
|
||||||
|
/// or write a custom filter.</param>
|
||||||
|
/// <returns>A <see cref="ILoggerFactory"/> for chaining additional operations.</returns>
|
||||||
|
public static ILoggerFactory AddApplicationInsights(
|
||||||
|
this ILoggerFactory loggerFactory,
|
||||||
|
string instrumentationKey,
|
||||||
|
Func<string, LogLevel, bool> filter)
|
||||||
|
{
|
||||||
|
return AddApplicationInsights(loggerFactory, instrumentationKey, filter, new DefaultTelemetryClientFactory(),
|
||||||
|
new SamplingPercentageEstimatorSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="ILoggerFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loggerFactory">The factory.</param>
|
||||||
|
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
|
||||||
|
/// <param name="filter">A filter that returns true if a message with the specified <see cref="LogLevel"/>
|
||||||
|
/// and category should be logged. You can use <see cref="LogCategoryFilter.Filter(string, LogLevel)"/>
|
||||||
|
/// or write a custom filter.</param>
|
||||||
|
/// <param name="telemetryClientFactory">The factory to use when creating the <see cref="TelemetryClient"/> </param>
|
||||||
|
/// <param name="samplingSettings">The adaptive sampling settings, or null to disable sampling.</param>
|
||||||
|
/// <returns>A <see cref="ILoggerFactory"/> for chaining additional operations.</returns>
|
||||||
|
public static ILoggerFactory AddApplicationInsights(
|
||||||
|
this ILoggerFactory loggerFactory,
|
||||||
|
string instrumentationKey,
|
||||||
|
Func<string, LogLevel, bool> filter,
|
||||||
|
ITelemetryClientFactory telemetryClientFactory,
|
||||||
|
SamplingPercentageEstimatorSettings samplingSettings)
|
||||||
|
{
|
||||||
|
if (loggerFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(loggerFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerFactory.AddProvider(new ApplicationInsightsLoggerProvider(instrumentationKey, filter, telemetryClientFactory, samplingSettings));
|
||||||
|
|
||||||
|
return loggerFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class ApplicationInsightsLoggerProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
private readonly TelemetryClient _client;
|
||||||
|
private readonly Func<string, LogLevel, bool> _filter;
|
||||||
|
|
||||||
|
public ApplicationInsightsLoggerProvider(string instrumentationKey, Func<string, LogLevel, bool> filter,
|
||||||
|
ITelemetryClientFactory clientFactory, SamplingPercentageEstimatorSettings samplingSettings)
|
||||||
|
{
|
||||||
|
if (instrumentationKey == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(instrumentationKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
_client = clientFactory.Create(instrumentationKey, samplingSettings);
|
||||||
|
_filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName) => new ApplicationInsightsLogger(_client, categoryName, _filter);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class DictionaryLoggerScope
|
||||||
|
{
|
||||||
|
private readonly object _state;
|
||||||
|
|
||||||
|
private DictionaryLoggerScope(object state, DictionaryLoggerScope parent)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal DictionaryLoggerScope Parent { get; private set; }
|
||||||
|
|
||||||
|
private static AsyncLocal<DictionaryLoggerScope> _value = new AsyncLocal<DictionaryLoggerScope>();
|
||||||
|
|
||||||
|
public static DictionaryLoggerScope Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _value.Value;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_value.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDisposable Push(object state)
|
||||||
|
{
|
||||||
|
Current = new DictionaryLoggerScope(state, Current);
|
||||||
|
return new DisposableScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a state dictionary of all scopes. If an inner scope
|
||||||
|
// contains the same key as an outer scope, it overwrites the value.
|
||||||
|
public static IDictionary<string, object> GetMergedStateDictionary()
|
||||||
|
{
|
||||||
|
IDictionary<string, object> scopeInfo = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
var current = Current;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
foreach (var entry in current.GetStateDictionary())
|
||||||
|
{
|
||||||
|
// inner scopes win
|
||||||
|
if (!scopeInfo.Keys.Contains(entry.Key))
|
||||||
|
{
|
||||||
|
scopeInfo.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<string, object> GetStateDictionary() => _state as IDictionary<string, object>;
|
||||||
|
|
||||||
|
private class DisposableScope : IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Current = Current.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility.Implementation;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="TelemetryClient"/> for use by the <see cref="ApplicationInsightsLogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
[CLSCompliant(false)]
|
||||||
|
public class DefaultTelemetryClientFactory : ITelemetryClientFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="TelemetryClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="TelemetryClient"/> instance.</returns>
|
||||||
|
public TelemetryClient Create(string instrumentationKey, SamplingPercentageEstimatorSettings samplingSettings)
|
||||||
|
{
|
||||||
|
TelemetryConfiguration config = InitializeConfiguration(instrumentationKey, samplingSettings);
|
||||||
|
|
||||||
|
TelemetryClient client = new TelemetryClient(config);
|
||||||
|
|
||||||
|
string assemblyVersion = GetAssemblyFileVersion(typeof(JobHost).Assembly);
|
||||||
|
client.Context.GetInternalContext().SdkVersion = $"webjobs: {assemblyVersion}";
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetAssemblyFileVersion(Assembly assembly)
|
||||||
|
{
|
||||||
|
AssemblyFileVersionAttribute fileVersionAttr = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>();
|
||||||
|
return fileVersionAttr?.Version ?? LoggingConstants.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TelemetryConfiguration InitializeConfiguration(string instrumentationKey,
|
||||||
|
SamplingPercentageEstimatorSettings samplingSettings)
|
||||||
|
{
|
||||||
|
TelemetryConfiguration config = new TelemetryConfiguration();
|
||||||
|
config.InstrumentationKey = instrumentationKey;
|
||||||
|
|
||||||
|
AddInitializers(config);
|
||||||
|
|
||||||
|
// Plug in Live stream and adaptive sampling
|
||||||
|
QuickPulseTelemetryProcessor processor = null;
|
||||||
|
TelemetryProcessorChainBuilder builder = config.TelemetryProcessorChainBuilder
|
||||||
|
.Use((next) =>
|
||||||
|
{
|
||||||
|
processor = new QuickPulseTelemetryProcessor(next);
|
||||||
|
return processor;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (samplingSettings != null)
|
||||||
|
{
|
||||||
|
builder.Use((next) =>
|
||||||
|
{
|
||||||
|
return new AdaptiveSamplingTelemetryProcessor(samplingSettings, null, next);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Build();
|
||||||
|
|
||||||
|
QuickPulseTelemetryModule quickPulse = new QuickPulseTelemetryModule();
|
||||||
|
quickPulse.Initialize(config);
|
||||||
|
quickPulse.RegisterTelemetryProcessor(processor);
|
||||||
|
|
||||||
|
// Plug in perf counters
|
||||||
|
PerformanceCollectorModule perfCounterCollectorModule = new PerformanceCollectorModule();
|
||||||
|
perfCounterCollectorModule.Initialize(config);
|
||||||
|
|
||||||
|
ServerTelemetryChannel channel = new ServerTelemetryChannel();
|
||||||
|
channel.Initialize(config);
|
||||||
|
config.TelemetryChannel = channel;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AddInitializers(TelemetryConfiguration config)
|
||||||
|
{
|
||||||
|
// This picks up the RoleName from the server
|
||||||
|
config.TelemetryInitializers.Add(new AzureWebAppRoleEnvironmentTelemetryInitializer());
|
||||||
|
|
||||||
|
// This applies our special scope properties and gets RoleInstance name
|
||||||
|
config.TelemetryInitializers.Add(new WebJobsTelemetryInitializer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for creating <see cref="TelemetryClient"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
[CLSCompliant(false)]
|
||||||
|
public interface ITelemetryClientFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="TelemetryClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
|
||||||
|
/// <param name="samplingSettings">The sampling settings, or null to disable sampling.</param>
|
||||||
|
/// <returns>The <see cref="TelemetryClient"/>. </returns>
|
||||||
|
TelemetryClient Create(string instrumentationKey, SamplingPercentageEstimatorSettings samplingSettings);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.ApplicationInsights.Channel;
|
||||||
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal class WebJobsTelemetryInitializer : ITelemetryInitializer
|
||||||
|
{
|
||||||
|
private const string ComputerNameKey = "COMPUTERNAME";
|
||||||
|
|
||||||
|
private static string _roleInstanceName = GetRoleInstanceName();
|
||||||
|
|
||||||
|
public void Initialize(ITelemetry telemetry)
|
||||||
|
{
|
||||||
|
if (telemetry == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetry.Context.Cloud.RoleInstance = _roleInstanceName;
|
||||||
|
|
||||||
|
// Zero out all IP addresses other than Requests
|
||||||
|
if (!(telemetry is RequestTelemetry))
|
||||||
|
{
|
||||||
|
telemetry.Context.Location.Ip = LoggingConstants.ZeroIpAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply our special scope properties
|
||||||
|
IDictionary<string, object> scopeProps = DictionaryLoggerScope.GetMergedStateDictionary() ?? new Dictionary<string, object>();
|
||||||
|
|
||||||
|
Guid invocationId = scopeProps.GetValueOrDefault<Guid>(ScopeKeys.FunctionInvocationId);
|
||||||
|
if (invocationId != default(Guid))
|
||||||
|
{
|
||||||
|
telemetry.Context.Operation.Id = invocationId.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
telemetry.Context.Operation.Name = scopeProps.GetValueOrDefault<string>(ScopeKeys.FunctionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRoleInstanceName()
|
||||||
|
{
|
||||||
|
string instanceName = Environment.GetEnvironmentVariable(WebSitesKnownKeyNames.WebSiteInstanceIdKey);
|
||||||
|
if (string.IsNullOrEmpty(instanceName))
|
||||||
|
{
|
||||||
|
instanceName = Environment.GetEnvironmentVariable(ComputerNameKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instanceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constant values for log categories.
|
||||||
|
/// </summary>
|
||||||
|
public static class LogCategories
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The category for all logs written by the function host during startup and shutdown. This
|
||||||
|
/// includes indexing and configuration logs.
|
||||||
|
/// </summary>
|
||||||
|
public const string Startup = "Host.Startup";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category for all logs written by the Singleton infrastructure.
|
||||||
|
/// </summary>
|
||||||
|
public const string Singleton = "Host.Singleton";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category for all logs written by the function executor.
|
||||||
|
/// </summary>
|
||||||
|
public const string Executor = "Host.Executor";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category for logs written by the function aggregator.
|
||||||
|
/// </summary>
|
||||||
|
public const string Aggregator = "Host.Aggregator";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category for function results.
|
||||||
|
/// </summary>
|
||||||
|
public const string Results = "Host.Results";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category for logs written from within user functions.
|
||||||
|
/// </summary>
|
||||||
|
public const string Function = "Function";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a logging category like "Host.Triggers.{triggerName}".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="triggerName">The trigger name.</param>
|
||||||
|
public static string CreateTriggerCategory(string triggerName)
|
||||||
|
{
|
||||||
|
return $"Host.Triggers.{triggerName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal static class LoggingConstants
|
||||||
|
{
|
||||||
|
public const string ZeroIpAddress = "0.0.0.0";
|
||||||
|
public const string Unknown = "[Unknown]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal static class LoggingKeys
|
||||||
|
{
|
||||||
|
// These are publicly visible as property names or prefixes
|
||||||
|
public const string FullName = "FullName";
|
||||||
|
public const string Name = "Name";
|
||||||
|
public const string Count = "Count";
|
||||||
|
public const string Successes = "Successes";
|
||||||
|
public const string Failures = "Failures";
|
||||||
|
public const string SuccessRate = "SuccessRate";
|
||||||
|
public const string AvgDuration = "AvgDurationMs";
|
||||||
|
public const string MaxDuration = "MaxDurationMs";
|
||||||
|
public const string MinDuration = "MinDurationMs";
|
||||||
|
public const string Timestamp = "Timestamp";
|
||||||
|
public const string InvocationId = "InvocationId";
|
||||||
|
public const string TriggerReason = "TriggerReason";
|
||||||
|
public const string StartTime = "StartTime";
|
||||||
|
public const string EndTime = "EndTime";
|
||||||
|
public const string Duration = "Duration";
|
||||||
|
public const string Succeeded = "Succeeded";
|
||||||
|
public const string FormattedMessage = "FormattedMessage";
|
||||||
|
public const string CategoryName = "Category";
|
||||||
|
public const string HttpMethod = "HttpMethod";
|
||||||
|
public const string CustomPropertyPrefix = "prop__";
|
||||||
|
public const string ParameterPrefix = "param__";
|
||||||
|
public const string OriginalFormat = "{OriginalFormat}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
internal static class ScopeKeys
|
||||||
|
{
|
||||||
|
// These are used internally for passing values via scopes
|
||||||
|
public const string FunctionInvocationId = "MS_FunctionInvocationId";
|
||||||
|
public const string FunctionName = "MS_FunctionName";
|
||||||
|
public const string HttpRequest = "MS_HttpRequest";
|
||||||
|
|
||||||
|
// HTTP context is set automatically by ASP.NET, this isn't ours.
|
||||||
|
internal const string HttpContext = "MS_HttpContext";
|
||||||
|
|
||||||
|
// This is set by Functions
|
||||||
|
internal const string FunctionsHttpResponse = "MS_AzureFunctionsHttpResponse";
|
||||||
|
|
||||||
|
internal const string ForwardedForHeaderName = "X-Forwarded-For";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a filter for use with an <see cref="ILogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
[System.CLSCompliant(false)]
|
||||||
|
public class LogCategoryFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum <see cref="LogLevel"/> required for logs with categories that do not match
|
||||||
|
/// any category in <see cref="CategoryLevels"/>.
|
||||||
|
/// </summary>
|
||||||
|
public LogLevel DefaultLevel { get; set; } = LogLevel.Information;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of filters that are used by <see cref="Filter"/> to determine if a log
|
||||||
|
/// will be written or filtered.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<string, LogLevel> CategoryLevels { get; } = new Dictionary<string, LogLevel> { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pass this function as a filter parameter to a <see cref="ILoggerProvider"/> to enable filtering
|
||||||
|
/// based on the specified <see cref="CategoryLevels"/>. The filter will match the longest possible key in
|
||||||
|
/// <see cref="CategoryLevels"/> and return true if the level is equal to or greater than the filter. If
|
||||||
|
/// there is no match, the value of <see cref="DefaultLevel"/> is used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="category">The category of the current log message.</param>
|
||||||
|
/// <param name="level">The <see cref="LogLevel"/> of the current log message.</param>
|
||||||
|
/// <returns>True if the level is equal to or greater than the matched filter. Otherwise, false.</returns>
|
||||||
|
public bool Filter(string category, LogLevel level)
|
||||||
|
{
|
||||||
|
// find the longest loglevel that matches the category
|
||||||
|
IEnumerable<string> matches = CategoryLevels.Keys.Where(k => category.StartsWith(k, System.StringComparison.CurrentCulture));
|
||||||
|
string longestMatch = matches?.Max();
|
||||||
|
|
||||||
|
LogLevel requestedLevel;
|
||||||
|
if (string.IsNullOrEmpty(longestMatch))
|
||||||
|
{
|
||||||
|
requestedLevel = DefaultLevel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestedLevel = CategoryLevels[longestMatch];
|
||||||
|
}
|
||||||
|
|
||||||
|
return level >= requestedLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="FormattedLogValues"/> object created by the framework <see cref="ILogger"/> extensions
|
||||||
|
/// (LogInformation(), for example) require that all key-value pairs be included in the string message
|
||||||
|
/// passed to the method. We'd like to have short strings with a subset of the data, so this class wraps
|
||||||
|
/// <see cref="FormattedLogValuesCollection"/> and allows us to use the same behavior, but with an additional
|
||||||
|
/// payload not included in the message.
|
||||||
|
/// </summary>
|
||||||
|
internal class FormattedLogValuesCollection : IReadOnlyList<KeyValuePair<string, object>>
|
||||||
|
{
|
||||||
|
private FormattedLogValues _formatter;
|
||||||
|
private IReadOnlyList<KeyValuePair<string, object>> _additionalValues;
|
||||||
|
|
||||||
|
public FormattedLogValuesCollection(string format, object[] formatValues, IReadOnlyDictionary<string, object> additionalValues)
|
||||||
|
{
|
||||||
|
if (formatValues != null)
|
||||||
|
{
|
||||||
|
_formatter = new FormattedLogValues(format, formatValues);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_formatter = new FormattedLogValues(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
_additionalValues = additionalValues?.ToList();
|
||||||
|
|
||||||
|
if (_additionalValues == null)
|
||||||
|
{
|
||||||
|
_additionalValues = new List<KeyValuePair<string, object>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _formatter.Count + _additionalValues.Count;
|
||||||
|
|
||||||
|
public KeyValuePair<string, object> this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= Count)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < _additionalValues.Count)
|
||||||
|
{
|
||||||
|
// if the index is lower, return the value from _additionalValues
|
||||||
|
return _additionalValues[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if there are no more additionalValues, return from _formatter
|
||||||
|
return _formatter[index - _additionalValues.Count];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Count; ++i)
|
||||||
|
{
|
||||||
|
yield return this[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public override string ToString() => _formatter.ToString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Logging
|
||||||
|
{
|
||||||
|
internal static class LoggerExtensions
|
||||||
|
{
|
||||||
|
// We want the short name for use with Application Insights.
|
||||||
|
internal static void LogFunctionResult(this ILogger logger, string shortName, FunctionInstanceLogEntry logEntry, TimeSpan duration, Exception exception = null)
|
||||||
|
{
|
||||||
|
bool succeeded = exception == null;
|
||||||
|
|
||||||
|
// build the string and values
|
||||||
|
string result = succeeded ? "Succeeded" : "Failed";
|
||||||
|
string logString = $"Executed '{{{LoggingKeys.FullName}}}' ({result}, Id={{{LoggingKeys.InvocationId}}})";
|
||||||
|
object[] values = new object[]
|
||||||
|
{
|
||||||
|
logEntry.FunctionName,
|
||||||
|
logEntry.FunctionInstanceId
|
||||||
|
};
|
||||||
|
|
||||||
|
// generate additional payload that is not in the string
|
||||||
|
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||||
|
properties.Add(LoggingKeys.Name, shortName);
|
||||||
|
properties.Add(LoggingKeys.TriggerReason, logEntry.TriggerReason);
|
||||||
|
properties.Add(LoggingKeys.StartTime, logEntry.StartTime);
|
||||||
|
properties.Add(LoggingKeys.EndTime, logEntry.EndTime);
|
||||||
|
properties.Add(LoggingKeys.Duration, duration);
|
||||||
|
properties.Add(LoggingKeys.Succeeded, succeeded);
|
||||||
|
|
||||||
|
if (logEntry.Arguments != null)
|
||||||
|
{
|
||||||
|
foreach (var arg in logEntry.Arguments)
|
||||||
|
{
|
||||||
|
properties.Add(LoggingKeys.ParameterPrefix + arg.Key, arg.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormattedLogValuesCollection payload = new FormattedLogValuesCollection(logString, values, new ReadOnlyDictionary<string, object>(properties));
|
||||||
|
LogLevel level = succeeded ? LogLevel.Information : LogLevel.Error;
|
||||||
|
logger.Log(level, 0, payload, exception, (s, e) => s.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void LogFunctionResultAggregate(this ILogger logger, FunctionResultAggregate resultAggregate)
|
||||||
|
{
|
||||||
|
// we won't output any string here, just the data
|
||||||
|
FormattedLogValuesCollection payload = new FormattedLogValuesCollection(string.Empty, null, resultAggregate.ToReadOnlyDictionary());
|
||||||
|
logger.Log(LogLevel.Information, 0, payload, null, (s, e) => s.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IDisposable BeginFunctionScope(this ILogger logger, IFunctionInstance functionInstance)
|
||||||
|
{
|
||||||
|
return logger?.BeginScope(
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
[ScopeKeys.FunctionInvocationId] = functionInstance?.Id,
|
||||||
|
[ScopeKeys.FunctionName] = functionInstance?.FunctionDescriptor?.Method?.Name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="TraceWriter"/> wrapper around an <see cref="ILogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal class LoggerTraceWriter : TraceWriter
|
||||||
|
{
|
||||||
|
private ILogger _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">The <see cref="TraceLevel"/> to use when filtering logs.</param>
|
||||||
|
/// <param name="logger">The <see cref="ILogger"/> used to log the traces.</param>
|
||||||
|
public LoggerTraceWriter(TraceLevel level, ILogger logger)
|
||||||
|
: base(level)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Trace(TraceEvent traceEvent)
|
||||||
|
{
|
||||||
|
if (traceEvent == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(traceEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceEvent.Level > Level)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogLevel level = GetLogLevel(traceEvent.Level);
|
||||||
|
FormattedLogValuesCollection logState = new FormattedLogValuesCollection(traceEvent.Message, null, new ReadOnlyDictionary<string, object>(traceEvent.Properties));
|
||||||
|
_logger.Log(level, 0, logState, traceEvent.Exception, (s, e) => s.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LogLevel GetLogLevel(TraceLevel traceLevel)
|
||||||
|
{
|
||||||
|
switch (traceLevel)
|
||||||
|
{
|
||||||
|
case TraceLevel.Off:
|
||||||
|
return LogLevel.None;
|
||||||
|
case TraceLevel.Error:
|
||||||
|
return LogLevel.Error;
|
||||||
|
case TraceLevel.Warning:
|
||||||
|
return LogLevel.Warning;
|
||||||
|
case TraceLevel.Info:
|
||||||
|
return LogLevel.Information;
|
||||||
|
case TraceLevel.Verbose:
|
||||||
|
return LogLevel.Debug;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"'{traceLevel}' is not a valid level.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
@ -18,11 +19,12 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
private readonly IReadOnlyDictionary<string, IWatcher> _watches;
|
private readonly IReadOnlyDictionary<string, IWatcher> _watches;
|
||||||
private readonly IStorageBlockBlob _parameterLogBlob;
|
private readonly IStorageBlockBlob _parameterLogBlob;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private string _lastContent;
|
private string _lastContent;
|
||||||
|
|
||||||
public UpdateParameterLogCommand(IReadOnlyDictionary<string, IWatcher> watches,
|
public UpdateParameterLogCommand(IReadOnlyDictionary<string, IWatcher> watches,
|
||||||
IStorageBlockBlob parameterLogBlob, TraceWriter trace)
|
IStorageBlockBlob parameterLogBlob, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
if (parameterLogBlob == null)
|
if (parameterLogBlob == null)
|
||||||
{
|
{
|
||||||
|
@ -39,6 +41,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
|
|
||||||
_parameterLogBlob = parameterLogBlob;
|
_parameterLogBlob = parameterLogBlob;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_logger = logger;
|
||||||
_watches = watches;
|
_watches = watches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +94,9 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
|
||||||
{
|
{
|
||||||
// Not fatal if we can't update parameter status.
|
// Not fatal if we can't update parameter status.
|
||||||
// But at least log what happened for diagnostics in case it's an infrastructure bug.
|
// But at least log what happened for diagnostics in case it's an infrastructure bug.
|
||||||
_trace.Error("---- Parameter status update failed ----", e, TraceSource.Execution);
|
string msg = "---- Parameter status update failed ----";
|
||||||
|
_trace.Error(msg, e, TraceSource.Execution);
|
||||||
|
_logger?.LogError(0, e, msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
private readonly IQueueConfiguration _queueConfiguration;
|
private readonly IQueueConfiguration _queueConfiguration;
|
||||||
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IFunctionIndexLookup _functionLookup;
|
private readonly IFunctionIndexLookup _functionLookup;
|
||||||
private readonly IFunctionInstanceLogger _functionInstanceLogger;
|
private readonly IFunctionInstanceLogger _functionInstanceLogger;
|
||||||
private readonly IFunctionExecutor _executor;
|
private readonly IFunctionExecutor _executor;
|
||||||
|
@ -27,6 +29,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
IQueueConfiguration queueConfiguration,
|
IQueueConfiguration queueConfiguration,
|
||||||
IWebJobsExceptionHandler exceptionHandler,
|
IWebJobsExceptionHandler exceptionHandler,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
IFunctionIndexLookup functionLookup,
|
IFunctionIndexLookup functionLookup,
|
||||||
IFunctionInstanceLogger functionInstanceLogger,
|
IFunctionInstanceLogger functionInstanceLogger,
|
||||||
IFunctionExecutor executor)
|
IFunctionExecutor executor)
|
||||||
|
@ -70,6 +73,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
_queueConfiguration = queueConfiguration;
|
_queueConfiguration = queueConfiguration;
|
||||||
_exceptionHandler = exceptionHandler;
|
_exceptionHandler = exceptionHandler;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_functionLookup = functionLookup;
|
_functionLookup = functionLookup;
|
||||||
_functionInstanceLogger = functionInstanceLogger;
|
_functionInstanceLogger = functionInstanceLogger;
|
||||||
_executor = executor;
|
_executor = executor;
|
||||||
|
@ -89,6 +93,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
triggerExecutor: triggerExecutor,
|
triggerExecutor: triggerExecutor,
|
||||||
exceptionHandler: _exceptionHandler,
|
exceptionHandler: _exceptionHandler,
|
||||||
trace: _trace,
|
trace: _trace,
|
||||||
|
loggerFactory: _loggerFactory,
|
||||||
sharedWatcher: null,
|
sharedWatcher: null,
|
||||||
queueConfiguration: _queueConfiguration,
|
queueConfiguration: _queueConfiguration,
|
||||||
maxPollingInterval: maxPollingInterval);
|
maxPollingInterval: maxPollingInterval);
|
||||||
|
|
|
@ -11,6 +11,7 @@ using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
|
||||||
|
@ -24,7 +25,6 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
private readonly IStorageQueue _poisonQueue;
|
private readonly IStorageQueue _poisonQueue;
|
||||||
private readonly ITriggerExecutor<IStorageQueueMessage> _triggerExecutor;
|
private readonly ITriggerExecutor<IStorageQueueMessage> _triggerExecutor;
|
||||||
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
||||||
private readonly TraceWriter _trace;
|
|
||||||
private readonly IMessageEnqueuedWatcher _sharedWatcher;
|
private readonly IMessageEnqueuedWatcher _sharedWatcher;
|
||||||
private readonly List<Task> _processing = new List<Task>();
|
private readonly List<Task> _processing = new List<Task>();
|
||||||
private readonly object _stopWaitingTaskSourceLock = new object();
|
private readonly object _stopWaitingTaskSourceLock = new object();
|
||||||
|
@ -41,6 +41,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
ITriggerExecutor<IStorageQueueMessage> triggerExecutor,
|
ITriggerExecutor<IStorageQueueMessage> triggerExecutor,
|
||||||
IWebJobsExceptionHandler exceptionHandler,
|
IWebJobsExceptionHandler exceptionHandler,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
SharedQueueWatcher sharedWatcher,
|
SharedQueueWatcher sharedWatcher,
|
||||||
IQueueConfiguration queueConfiguration,
|
IQueueConfiguration queueConfiguration,
|
||||||
QueueProcessor queueProcessor = null,
|
QueueProcessor queueProcessor = null,
|
||||||
|
@ -71,7 +72,6 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
_poisonQueue = poisonQueue;
|
_poisonQueue = poisonQueue;
|
||||||
_triggerExecutor = triggerExecutor;
|
_triggerExecutor = triggerExecutor;
|
||||||
_exceptionHandler = exceptionHandler;
|
_exceptionHandler = exceptionHandler;
|
||||||
_trace = trace;
|
|
||||||
_queueConfiguration = queueConfiguration;
|
_queueConfiguration = queueConfiguration;
|
||||||
|
|
||||||
// if the function runs longer than this, the invisibility will be updated
|
// if the function runs longer than this, the invisibility will be updated
|
||||||
|
@ -88,7 +88,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
EventHandler<PoisonMessageEventArgs> poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler<PoisonMessageEventArgs>)null;
|
EventHandler<PoisonMessageEventArgs> poisonMessageEventHandler = _sharedWatcher != null ? OnMessageAddedToPoisonQueue : (EventHandler<PoisonMessageEventArgs>)null;
|
||||||
_queueProcessor = queueProcessor ?? CreateQueueProcessor(
|
_queueProcessor = queueProcessor ?? CreateQueueProcessor(
|
||||||
_queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null,
|
_queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null,
|
||||||
_trace, _queueConfiguration, poisonMessageEventHandler);
|
trace, loggerFactory, _queueConfiguration, poisonMessageEventHandler);
|
||||||
|
|
||||||
TimeSpan maximumInterval = _queueProcessor.MaxPollingInterval;
|
TimeSpan maximumInterval = _queueProcessor.MaxPollingInterval;
|
||||||
if (maxPollingInterval.HasValue && maximumInterval > maxPollingInterval.Value)
|
if (maxPollingInterval.HasValue && maximumInterval > maxPollingInterval.Value)
|
||||||
|
@ -309,9 +309,10 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static QueueProcessor CreateQueueProcessor(CloudQueue queue, CloudQueue poisonQueue, TraceWriter trace, IQueueConfiguration queueConfig, EventHandler<PoisonMessageEventArgs> poisonQueueMessageAddedHandler)
|
internal static QueueProcessor CreateQueueProcessor(CloudQueue queue, CloudQueue poisonQueue, TraceWriter trace, ILoggerFactory loggerFactory,
|
||||||
|
IQueueConfiguration queueConfig, EventHandler<PoisonMessageEventArgs> poisonQueueMessageAddedHandler)
|
||||||
{
|
{
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(queue, trace, queueConfig, poisonQueue);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(queue, trace, loggerFactory, queueConfig, poisonQueue);
|
||||||
|
|
||||||
QueueProcessor queueProcessor = null;
|
QueueProcessor queueProcessor = null;
|
||||||
if (HostQueueNames.IsHostQueue(queue.Name) &&
|
if (HostQueueNames.IsHostQueue(queue.Name) &&
|
||||||
|
|
|
@ -9,6 +9,7 @@ using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly ITriggeredFunctionExecutor _executor;
|
private readonly ITriggeredFunctionExecutor _executor;
|
||||||
|
|
||||||
public QueueListenerFactory(IStorageQueue queue,
|
public QueueListenerFactory(IStorageQueue queue,
|
||||||
|
@ -31,6 +33,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
TraceWriter trace,
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
ITriggeredFunctionExecutor executor)
|
ITriggeredFunctionExecutor executor)
|
||||||
{
|
{
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
|
@ -75,6 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_executor = executor;
|
_executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +91,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
|
||||||
new SharedQueueWatcherFactory(_messageEnqueuedWatcherSetter));
|
new SharedQueueWatcherFactory(_messageEnqueuedWatcherSetter));
|
||||||
|
|
||||||
IListener listener = new QueueListener(_queue, _poisonQueue, triggerExecutor,
|
IListener listener = new QueueListener(_queue, _poisonQueue, triggerExecutor,
|
||||||
_exceptionHandler, _trace, sharedWatcher, _queueConfiguration);
|
_exceptionHandler, _trace, _loggerFactory, sharedWatcher, _queueConfiguration);
|
||||||
|
|
||||||
return Task.FromResult(listener);
|
return Task.FromResult(listener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
private readonly CloudQueue _queue;
|
private readonly CloudQueue _queue;
|
||||||
private readonly CloudQueue _poisonQueue;
|
private readonly CloudQueue _poisonQueue;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance.
|
/// Constructs a new instance.
|
||||||
|
@ -41,6 +43,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
_queue = context.Queue;
|
_queue = context.Queue;
|
||||||
_poisonQueue = context.PoisonQueue;
|
_poisonQueue = context.PoisonQueue;
|
||||||
_trace = context.Trace;
|
_trace = context.Trace;
|
||||||
|
_logger = context.Logger;
|
||||||
|
|
||||||
MaxDequeueCount = context.MaxDequeueCount;
|
MaxDequeueCount = context.MaxDequeueCount;
|
||||||
BatchSize = context.BatchSize;
|
BatchSize = context.BatchSize;
|
||||||
|
@ -106,7 +109,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param>
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual async Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken)
|
public virtual async Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
await DeleteMessageAsync(message, cancellationToken);
|
await DeleteMessageAsync(message, cancellationToken);
|
||||||
|
@ -140,7 +143,9 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected virtual async Task CopyMessageToPoisonQueueAsync(CloudQueueMessage message, CloudQueue poisonQueue, CancellationToken cancellationToken)
|
protected virtual async Task CopyMessageToPoisonQueueAsync(CloudQueueMessage message, CloudQueue poisonQueue, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_trace.Warning(string.Format(CultureInfo.InvariantCulture, "Message has reached MaxDequeueCount of {0}. Moving message to queue '{1}'.", MaxDequeueCount, poisonQueue.Name), TraceSource.Execution);
|
string msg = string.Format(CultureInfo.InvariantCulture, "Message has reached MaxDequeueCount of {0}. Moving message to queue '{1}'.", MaxDequeueCount, poisonQueue.Name);
|
||||||
|
_trace.Warning(msg, TraceSource.Execution);
|
||||||
|
_logger?.LogWarning(msg);
|
||||||
|
|
||||||
await AddMessageAndCreateIfNotExistsAsync(poisonQueue, message, cancellationToken);
|
await AddMessageAndCreateIfNotExistsAsync(poisonQueue, message, cancellationToken);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Queues
|
namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
|
@ -17,8 +19,9 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="queue">The <see cref="CloudQueue"/> the <see cref="QueueProcessor"/> will operate on.</param>
|
/// <param name="queue">The <see cref="CloudQueue"/> the <see cref="QueueProcessor"/> will operate on.</param>
|
||||||
/// <param name="trace">The <see cref="TraceWriter"/> to trace events to.</param>
|
/// <param name="trace">The <see cref="TraceWriter"/> to trace events to.</param>
|
||||||
|
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to create an <see cref="ILogger"/> from.</param>
|
||||||
/// <param name="poisonQueue">The queue to move messages to when unable to process a message after the maximum dequeue count has been exceeded. May be null.</param>
|
/// <param name="poisonQueue">The queue to move messages to when unable to process a message after the maximum dequeue count has been exceeded. May be null.</param>
|
||||||
public QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, CloudQueue poisonQueue = null)
|
public QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, ILoggerFactory loggerFactory, CloudQueue poisonQueue = null)
|
||||||
{
|
{
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
{
|
{
|
||||||
|
@ -32,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
Queue = queue;
|
Queue = queue;
|
||||||
PoisonQueue = poisonQueue;
|
PoisonQueue = poisonQueue;
|
||||||
Trace = trace;
|
Trace = trace;
|
||||||
|
Logger = loggerFactory?.CreateLogger(LogCategories.CreateTriggerCategory("Queue"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -39,10 +43,11 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="queue">The <see cref="CloudQueue"/> the <see cref="QueueProcessor"/> will operate on.</param>
|
/// <param name="queue">The <see cref="CloudQueue"/> the <see cref="QueueProcessor"/> will operate on.</param>
|
||||||
/// <param name="trace">The <see cref="TraceWriter"/> to write to.</param>
|
/// <param name="trace">The <see cref="TraceWriter"/> to write to.</param>
|
||||||
|
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to create an <see cref="ILogger"/> from.</param>
|
||||||
/// <param name="queueConfiguration">The queue configuration.</param>
|
/// <param name="queueConfiguration">The queue configuration.</param>
|
||||||
/// <param name="poisonQueue">The queue to move messages to when unable to process a message after the maximum dequeue count has been exceeded. May be null.</param>
|
/// <param name="poisonQueue">The queue to move messages to when unable to process a message after the maximum dequeue count has been exceeded. May be null.</param>
|
||||||
internal QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, IQueueConfiguration queueConfiguration, CloudQueue poisonQueue = null)
|
internal QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, ILoggerFactory loggerFactory, IQueueConfiguration queueConfiguration, CloudQueue poisonQueue = null)
|
||||||
: this(queue, trace, poisonQueue)
|
: this(queue, trace, loggerFactory, poisonQueue)
|
||||||
{
|
{
|
||||||
BatchSize = queueConfiguration.BatchSize;
|
BatchSize = queueConfiguration.BatchSize;
|
||||||
MaxDequeueCount = queueConfiguration.MaxDequeueCount;
|
MaxDequeueCount = queueConfiguration.MaxDequeueCount;
|
||||||
|
@ -67,6 +72,11 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TraceWriter Trace { get; private set; }
|
public TraceWriter Trace { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ILogger"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ILogger Logger { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the number of queue messages to retrieve and process in parallel (per job method).
|
/// Gets or sets the number of queue messages to retrieve and process in parallel (per job method).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
|
@ -31,6 +32,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
public QueueTriggerAttributeBindingProvider(INameResolver nameResolver,
|
public QueueTriggerAttributeBindingProvider(INameResolver nameResolver,
|
||||||
IStorageAccountProvider accountProvider,
|
IStorageAccountProvider accountProvider,
|
||||||
|
@ -38,7 +40,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
IWebJobsExceptionHandler exceptionHandler,
|
IWebJobsExceptionHandler exceptionHandler,
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (accountProvider == null)
|
if (accountProvider == null)
|
||||||
{
|
{
|
||||||
|
@ -77,6 +80,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context)
|
public async Task<ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context)
|
||||||
|
@ -113,7 +117,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
|
|
||||||
ITriggerBinding binding = new QueueTriggerBinding(parameter.Name, queue, argumentBinding,
|
ITriggerBinding binding = new QueueTriggerBinding(parameter.Name, queue, argumentBinding,
|
||||||
_queueConfiguration, _exceptionHandler, _messageEnqueuedWatcherSetter,
|
_queueConfiguration, _exceptionHandler, _messageEnqueuedWatcherSetter,
|
||||||
_sharedContextProvider, _trace);
|
_sharedContextProvider, _trace, _loggerFactory);
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ using Microsoft.Azure.WebJobs.Host.Queues.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
|
@ -27,6 +28,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
private readonly IContextSetter<IMessageEnqueuedWatcher> _messageEnqueuedWatcherSetter;
|
||||||
private readonly ISharedContextProvider _sharedContextProvider;
|
private readonly ISharedContextProvider _sharedContextProvider;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IObjectToTypeConverter<IStorageQueueMessage> _converter;
|
private readonly IObjectToTypeConverter<IStorageQueueMessage> _converter;
|
||||||
|
|
||||||
public QueueTriggerBinding(string parameterName,
|
public QueueTriggerBinding(string parameterName,
|
||||||
|
@ -36,7 +38,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
IWebJobsExceptionHandler exceptionHandler,
|
IWebJobsExceptionHandler exceptionHandler,
|
||||||
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
|
||||||
ISharedContextProvider sharedContextProvider,
|
ISharedContextProvider sharedContextProvider,
|
||||||
TraceWriter trace)
|
TraceWriter trace,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
if (queue == null)
|
if (queue == null)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +85,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
|
||||||
_sharedContextProvider = sharedContextProvider;
|
_sharedContextProvider = sharedContextProvider;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_converter = CreateConverter(queue);
|
_converter = CreateConverter(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,8 +160,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
throw new ArgumentNullException("context");
|
throw new ArgumentNullException("context");
|
||||||
}
|
}
|
||||||
|
|
||||||
var factory = new QueueListenerFactory(_queue, _queueConfiguration, _exceptionHandler,
|
var factory = new QueueListenerFactory(_queue, _queueConfiguration, _exceptionHandler,
|
||||||
_messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, context.Executor);
|
_messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, _loggerFactory, context.Executor);
|
||||||
|
|
||||||
return factory.CreateAsync(context.CancellationToken);
|
return factory.CreateAsync(context.CancellationToken);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +195,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
|
||||||
bindingData.Add("InsertionTime", value.InsertionTime.GetValueOrDefault(DateTimeOffset.UtcNow));
|
bindingData.Add("InsertionTime", value.InsertionTime.GetValueOrDefault(DateTimeOffset.UtcNow));
|
||||||
bindingData.Add("NextVisibleTime", value.NextVisibleTime.GetValueOrDefault(DateTimeOffset.MaxValue));
|
bindingData.Add("NextVisibleTime", value.NextVisibleTime.GetValueOrDefault(DateTimeOffset.MaxValue));
|
||||||
bindingData.Add("PopReceipt", value.PopReceipt);
|
bindingData.Add("PopReceipt", value.PopReceipt);
|
||||||
|
|
||||||
if (bindingDataFromValueType != null)
|
if (bindingDataFromValueType != null)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<string, object> item in bindingDataFromValueType)
|
foreach (KeyValuePair<string, object> item in bindingDataFromValueType)
|
||||||
|
|
|
@ -7,6 +7,8 @@ using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
{
|
{
|
||||||
|
@ -17,11 +19,12 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
private readonly SingletonConfiguration _singletonConfig;
|
private readonly SingletonConfiguration _singletonConfig;
|
||||||
private readonly IListener _innerListener;
|
private readonly IListener _innerListener;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private string _lockId;
|
private string _lockId;
|
||||||
private object _lockHandle;
|
private object _lockHandle;
|
||||||
private bool _isListening;
|
private bool _isListening;
|
||||||
|
|
||||||
public SingletonListener(MethodInfo method, SingletonAttribute attribute, SingletonManager singletonManager, IListener innerListener, TraceWriter trace)
|
public SingletonListener(MethodInfo method, SingletonAttribute attribute, SingletonManager singletonManager, IListener innerListener, TraceWriter trace, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_attribute = attribute;
|
_attribute = attribute;
|
||||||
_singletonManager = singletonManager;
|
_singletonManager = singletonManager;
|
||||||
|
@ -33,6 +36,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
_lockId += ".Listener";
|
_lockId += ".Listener";
|
||||||
|
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_logger = loggerFactory?.CreateLogger(LogCategories.Singleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
// exposed for testing
|
// exposed for testing
|
||||||
|
@ -47,7 +51,9 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
|
||||||
|
|
||||||
if (_lockHandle == null)
|
if (_lockHandle == null)
|
||||||
{
|
{
|
||||||
_trace.Verbose(string.Format(CultureInfo.InvariantCulture, "Unable to acquire Singleton lock ({0}).", _lockId), source: TraceSource.Execution);
|
string msg = string.Format(CultureInfo.InvariantCulture, "Unable to acquire Singleton lock ({0}).", _lockId);
|
||||||
|
_trace.Verbose(msg, source: TraceSource.Execution);
|
||||||
|
_logger?.LogDebug(msg);
|
||||||
|
|
||||||
// If we're unable to acquire the lock, it means another listener
|
// If we're unable to acquire the lock, it means another listener
|
||||||
// has it so we return w/o starting our listener.
|
// has it so we return w/o starting our listener.
|
||||||
|
|
|
@ -14,10 +14,12 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
|
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
|
|
||||||
|
@ -33,6 +35,8 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
private readonly IWebJobsExceptionHandler _exceptionHandler;
|
||||||
private readonly SingletonConfiguration _config;
|
private readonly SingletonConfiguration _config;
|
||||||
private readonly IStorageAccountProvider _accountProvider;
|
private readonly IStorageAccountProvider _accountProvider;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private ConcurrentDictionary<string, IStorageBlobDirectory> _lockDirectoryMap = new ConcurrentDictionary<string, IStorageBlobDirectory>(StringComparer.OrdinalIgnoreCase);
|
private ConcurrentDictionary<string, IStorageBlobDirectory> _lockDirectoryMap = new ConcurrentDictionary<string, IStorageBlobDirectory>(StringComparer.OrdinalIgnoreCase);
|
||||||
private TimeSpan _minimumLeaseRenewalInterval = TimeSpan.FromSeconds(1);
|
private TimeSpan _minimumLeaseRenewalInterval = TimeSpan.FromSeconds(1);
|
||||||
private TraceWriter _trace;
|
private TraceWriter _trace;
|
||||||
|
@ -44,13 +48,16 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingletonManager(IStorageAccountProvider accountProvider, IWebJobsExceptionHandler exceptionHandler, SingletonConfiguration config, TraceWriter trace, IHostIdProvider hostIdProvider, INameResolver nameResolver = null)
|
public SingletonManager(IStorageAccountProvider accountProvider, IWebJobsExceptionHandler exceptionHandler, SingletonConfiguration config,
|
||||||
|
TraceWriter trace, ILoggerFactory loggerFactory, IHostIdProvider hostIdProvider, INameResolver nameResolver = null)
|
||||||
{
|
{
|
||||||
_accountProvider = accountProvider;
|
_accountProvider = accountProvider;
|
||||||
_nameResolver = nameResolver;
|
_nameResolver = nameResolver;
|
||||||
_exceptionHandler = exceptionHandler;
|
_exceptionHandler = exceptionHandler;
|
||||||
_config = config;
|
_config = config;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_logger = _loggerFactory?.CreateLogger(LogCategories.Singleton);
|
||||||
_hostIdProvider = hostIdProvider;
|
_hostIdProvider = hostIdProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +137,9 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_trace.Verbose(string.Format(CultureInfo.InvariantCulture, "Singleton lock acquired ({0})", lockId), source: TraceSource.Execution);
|
string msg = string.Format(CultureInfo.InvariantCulture, "Singleton lock acquired ({0})", lockId);
|
||||||
|
_trace.Verbose(msg, source: TraceSource.Execution);
|
||||||
|
_logger?.LogDebug(msg);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(functionInstanceId))
|
if (!string.IsNullOrEmpty(functionInstanceId))
|
||||||
{
|
{
|
||||||
|
@ -163,7 +172,9 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
|
|
||||||
await ReleaseLeaseAsync(singletonLockHandle.Blob, singletonLockHandle.LeaseId, cancellationToken);
|
await ReleaseLeaseAsync(singletonLockHandle.Blob, singletonLockHandle.LeaseId, cancellationToken);
|
||||||
|
|
||||||
_trace.Verbose(string.Format(CultureInfo.InvariantCulture, "Singleton lock released ({0})", singletonLockHandle.LockId), source: TraceSource.Execution);
|
string msg = string.Format(CultureInfo.InvariantCulture, "Singleton lock released ({0})", singletonLockHandle.LockId);
|
||||||
|
_trace.Verbose(msg, source: TraceSource.Execution);
|
||||||
|
_logger?.LogDebug(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FormatLockId(MethodInfo method, SingletonScope scope, string scopeId)
|
public string FormatLockId(MethodInfo method, SingletonScope scope, string scopeId)
|
||||||
|
@ -251,7 +262,7 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
{
|
{
|
||||||
Mode = SingletonMode.Listener
|
Mode = SingletonMode.Listener
|
||||||
};
|
};
|
||||||
return new SingletonListener(null, singletonAttribute, this, innerListener, _trace);
|
return new SingletonListener(null, singletonAttribute, this, innerListener, _trace, _loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SingletonAttribute GetListenerSingletonOrNull(Type listenerType, MethodInfo method)
|
public static SingletonAttribute GetListenerSingletonOrNull(Type listenerType, MethodInfo method)
|
||||||
|
@ -351,7 +362,7 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
TimeSpan normalUpdateInterval = new TimeSpan(leasePeriod.Ticks / 2);
|
TimeSpan normalUpdateInterval = new TimeSpan(leasePeriod.Ticks / 2);
|
||||||
|
|
||||||
IDelayStrategy speedupStrategy = new LinearSpeedupStrategy(normalUpdateInterval, MinimumLeaseRenewalInterval);
|
IDelayStrategy speedupStrategy = new LinearSpeedupStrategy(normalUpdateInterval, MinimumLeaseRenewalInterval);
|
||||||
ITaskSeriesCommand command = new RenewLeaseCommand(leaseBlob, leaseId, lockId, speedupStrategy, _trace, leasePeriod);
|
ITaskSeriesCommand command = new RenewLeaseCommand(leaseBlob, leaseId, lockId, speedupStrategy, _trace, _logger, leasePeriod);
|
||||||
return new TaskSeriesTimer(command, exceptionHandler, Task.Delay(normalUpdateInterval));
|
return new TaskSeriesTimer(command, exceptionHandler, Task.Delay(normalUpdateInterval));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,11 +561,13 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
private readonly string _lockId;
|
private readonly string _lockId;
|
||||||
private readonly IDelayStrategy _speedupStrategy;
|
private readonly IDelayStrategy _speedupStrategy;
|
||||||
private readonly TraceWriter _trace;
|
private readonly TraceWriter _trace;
|
||||||
|
private readonly ILogger _logger;
|
||||||
private DateTimeOffset _lastRenewal;
|
private DateTimeOffset _lastRenewal;
|
||||||
private TimeSpan _lastRenewalLatency;
|
private TimeSpan _lastRenewalLatency;
|
||||||
private TimeSpan _leasePeriod;
|
private TimeSpan _leasePeriod;
|
||||||
|
|
||||||
public RenewLeaseCommand(IStorageBlockBlob leaseBlob, string leaseId, string lockId, IDelayStrategy speedupStrategy, TraceWriter trace, TimeSpan leasePeriod)
|
public RenewLeaseCommand(IStorageBlockBlob leaseBlob, string leaseId, string lockId, IDelayStrategy speedupStrategy, TraceWriter trace,
|
||||||
|
ILogger logger, TimeSpan leasePeriod)
|
||||||
{
|
{
|
||||||
_lastRenewal = DateTimeOffset.UtcNow;
|
_lastRenewal = DateTimeOffset.UtcNow;
|
||||||
_leaseBlob = leaseBlob;
|
_leaseBlob = leaseBlob;
|
||||||
|
@ -562,6 +575,7 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
_lockId = lockId;
|
_lockId = lockId;
|
||||||
_speedupStrategy = speedupStrategy;
|
_speedupStrategy = speedupStrategy;
|
||||||
_trace = trace;
|
_trace = trace;
|
||||||
|
_logger = logger;
|
||||||
_leasePeriod = leasePeriod;
|
_leasePeriod = leasePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,8 +603,10 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
{
|
{
|
||||||
// The next execution should occur more quickly (try to renew the lease before it expires).
|
// The next execution should occur more quickly (try to renew the lease before it expires).
|
||||||
delay = _speedupStrategy.GetNextDelay(executionSucceeded: false);
|
delay = _speedupStrategy.GetNextDelay(executionSucceeded: false);
|
||||||
_trace.Warning(string.Format(CultureInfo.InvariantCulture, "Singleton lock renewal failed for blob '{0}' with error code {1}. Retry renewal in {2} milliseconds.",
|
string msg = string.Format(CultureInfo.InvariantCulture, "Singleton lock renewal failed for blob '{0}' with error code {1}. Retry renewal in {2} milliseconds.",
|
||||||
_lockId, FormatErrorCode(exception), delay.TotalMilliseconds), source: TraceSource.Execution);
|
_lockId, FormatErrorCode(exception), delay.TotalMilliseconds);
|
||||||
|
_trace.Warning(msg, source: TraceSource.Execution);
|
||||||
|
_logger?.LogWarning(msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -600,8 +616,10 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
int millisecondsSinceLastSuccess = (int)(DateTime.UtcNow - _lastRenewal).TotalMilliseconds;
|
int millisecondsSinceLastSuccess = (int)(DateTime.UtcNow - _lastRenewal).TotalMilliseconds;
|
||||||
int lastRenewalMilliseconds = (int)_lastRenewalLatency.TotalMilliseconds;
|
int lastRenewalMilliseconds = (int)_lastRenewalLatency.TotalMilliseconds;
|
||||||
|
|
||||||
_trace.Error(string.Format(CultureInfo.InvariantCulture, "Singleton lock renewal failed for blob '{0}' with error code {1}. The last successful renewal completed at {2} ({3} milliseconds ago) with a duration of {4} milliseconds. The lease period was {5} milliseconds.",
|
string msg = string.Format(CultureInfo.InvariantCulture, "Singleton lock renewal failed for blob '{0}' with error code {1}. The last successful renewal completed at {2} ({3} milliseconds ago) with a duration of {4} milliseconds. The lease period was {5} milliseconds.",
|
||||||
_lockId, FormatErrorCode(exception), lastRenewalFormatted, millisecondsSinceLastSuccess, lastRenewalMilliseconds, leasePeriodMilliseconds));
|
_lockId, FormatErrorCode(exception), lastRenewalFormatted, millisecondsSinceLastSuccess, lastRenewalMilliseconds, leasePeriodMilliseconds);
|
||||||
|
_trace.Error(msg);
|
||||||
|
_logger?.LogError(msg);
|
||||||
|
|
||||||
// If we've lost the lease or cannot re-establish it, we want to fail any
|
// If we've lost the lease or cannot re-establish it, we want to fail any
|
||||||
// in progress function execution
|
// in progress function execution
|
||||||
|
|
|
@ -54,6 +54,30 @@
|
||||||
<AssemblyOriginatorKeyFile>..\Common\PublicKey.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>..\Common\PublicKey.snk</AssemblyOriginatorKeyFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.AI.Agent.Intercept, Version=2.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.0.7\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AI.DependencyCollector, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.3.0\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.3.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AI.ServerTelemetryChannel, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.3.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.AI.WindowsServer, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.WindowsServer.2.3.0\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.ApplicationInsights, Version=2.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.ApplicationInsights.2.3.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -70,6 +94,14 @@
|
||||||
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -83,19 +115,90 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Numerics" />
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Runtime.Serialization" />
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.ServiceModel" />
|
<Reference Include="System.ServiceModel" />
|
||||||
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\Common\CommonAssemblyInfo.cs">
|
<Compile Include="..\Common\CommonAssemblyInfo.cs">
|
||||||
|
@ -393,6 +496,8 @@
|
||||||
<Compile Include="Bindings\DefaultAttributeInvokerDescriptor.cs" />
|
<Compile Include="Bindings\DefaultAttributeInvokerDescriptor.cs" />
|
||||||
<Compile Include="Bindings\FuncArgumentBuilder.cs" />
|
<Compile Include="Bindings\FuncArgumentBuilder.cs" />
|
||||||
<Compile Include="Bindings\IArgumentBindingProvider.cs" />
|
<Compile Include="Bindings\IArgumentBindingProvider.cs" />
|
||||||
|
<Compile Include="Bindings\Logger\LoggerBinding.cs" />
|
||||||
|
<Compile Include="Bindings\Logger\LoggerBindingProvider.cs" />
|
||||||
<Compile Include="Bindings\OpenType.cs" />
|
<Compile Include="Bindings\OpenType.cs" />
|
||||||
<Compile Include="Bindings\Path\BindingParameterResolver.cs" />
|
<Compile Include="Bindings\Path\BindingParameterResolver.cs" />
|
||||||
<Compile Include="Bindings\PatternMatcher.cs" />
|
<Compile Include="Bindings\PatternMatcher.cs" />
|
||||||
|
@ -408,14 +513,36 @@
|
||||||
<Compile Include="Config\ServiceProviderWrapper.cs" />
|
<Compile Include="Config\ServiceProviderWrapper.cs" />
|
||||||
<Compile Include="DefaultNameResolver.cs" />
|
<Compile Include="DefaultNameResolver.cs" />
|
||||||
<Compile Include="Diagnostics\ExceptionFormatter.cs" />
|
<Compile Include="Diagnostics\ExceptionFormatter.cs" />
|
||||||
|
<Compile Include="Executors\CompositeFunctionEventCollector.cs" />
|
||||||
<Compile Include="Executors\FunctionInstanceTraceWriter.cs" />
|
<Compile Include="Executors\FunctionInstanceTraceWriter.cs" />
|
||||||
<Compile Include="Extensions\IJobHostMetadataProvider.cs" />
|
<Compile Include="Extensions\IJobHostMetadataProvider.cs" />
|
||||||
<Compile Include="Extensions\JobHostMetadataProvider.cs" />
|
<Compile Include="Extensions\JobHostMetadataProvider.cs" />
|
||||||
|
<Compile Include="Extensions\DictionaryExtensions.cs" />
|
||||||
<Compile Include="FuncConverter.cs" />
|
<Compile Include="FuncConverter.cs" />
|
||||||
<Compile Include="FunctionTimeoutException.cs" />
|
<Compile Include="FunctionTimeoutException.cs" />
|
||||||
<Compile Include="Exceptions\RecoverableException.cs" />
|
<Compile Include="Exceptions\RecoverableException.cs" />
|
||||||
<Compile Include="JobHostBlobsConfiguration.cs" />
|
<Compile Include="JobHostBlobsConfiguration.cs" />
|
||||||
<Compile Include="Listeners\FunctionListener.cs" />
|
<Compile Include="Listeners\FunctionListener.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Aggregator\FunctionResultAggregatorConfiguration.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Aggregator\FunctionResultAggregate.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Aggregator\FunctionResultAggregator.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Aggregator\FunctionResultAggregatorFactory.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Aggregator\IFunctionResultAggregatorFactory.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\ApplicationInsightsLogger.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\ApplicationInsightsLoggerExtensions.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\ApplicationInsightsLoggerProvider.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\ApplicationInsightsScope.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\ITelemetryClientFactory.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\DefaultTelemetryClientFactory.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\ApplicationInsights\WebJobsTelemetryInitializer.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Constants\LogCategories.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Constants\LoggingConstants.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Constants\LoggingKeys.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\Constants\ScopeKeys.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\LoggerExtensions.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\LogCategoryFilter.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\LogValueCollection.cs" />
|
||||||
|
<Compile Include="Loggers\Logger\TraceWriter\LoggerTraceWriter.cs" />
|
||||||
<Compile Include="Loggers\TraceEventExtensions.cs" />
|
<Compile Include="Loggers\TraceEventExtensions.cs" />
|
||||||
<Compile Include="Exceptions\FunctionException.cs" />
|
<Compile Include="Exceptions\FunctionException.cs" />
|
||||||
<Compile Include="Queues\Bindings\QueueBindingProvider.cs" />
|
<Compile Include="Queues\Bindings\QueueBindingProvider.cs" />
|
||||||
|
|
|
@ -6,5 +6,7 @@ namespace Microsoft.Azure.WebJobs.Host
|
||||||
internal static class WebSitesKnownKeyNames
|
internal static class WebSitesKnownKeyNames
|
||||||
{
|
{
|
||||||
public const string JobDataPath = "WEBJOBS_DATA_PATH";
|
public const string JobDataPath = "WEBJOBS_DATA_PATH";
|
||||||
|
|
||||||
|
public const string WebSiteInstanceIdKey = "WEBSITE_INSTANCE_ID";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -1,17 +1,71 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="Microsoft.ApplicationInsights" version="2.3.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.0.7" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.3.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.3.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.3.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.3.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net45" />
|
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Tpl.Dataflow" version="4.5.24" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
||||||
|
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||||
<package id="StyleCop.MSBuild" version="4.7.55.0" targetFramework="net45" developmentDependency="true" />
|
<package id="StyleCop.MSBuild" version="4.7.55.0" targetFramework="net45" developmentDependency="true" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Http" version="4.3.1" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ObjectModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
||||||
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
|
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
|
@ -17,6 +17,8 @@
|
||||||
<dependency id="Microsoft.Azure.WebJobs.Core" version="[$WebJobsPackageVersion$]" />
|
<dependency id="Microsoft.Azure.WebJobs.Core" version="[$WebJobsPackageVersion$]" />
|
||||||
<dependency id="Newtonsoft.Json" version="9.0.1"/>
|
<dependency id="Newtonsoft.Json" version="9.0.1"/>
|
||||||
<dependency id="WindowsAzure.Storage" version="7.2.1"/>
|
<dependency id="WindowsAzure.Storage" version="7.2.1"/>
|
||||||
|
<dependency id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" />
|
||||||
|
<dependency id="Microsoft.ApplicationInsights.WindowsServer" version="2.3.0" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
</package>
|
</package>
|
|
@ -1,30 +1,30 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
@ -32,30 +32,30 @@
|
||||||
<extensions>
|
<extensions>
|
||||||
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
|
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
|
||||||
<behaviorExtensions>
|
<behaviorExtensions>
|
||||||
<add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</behaviorExtensions>
|
</behaviorExtensions>
|
||||||
<bindingElementExtensions>
|
<bindingElementExtensions>
|
||||||
<add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</bindingElementExtensions>
|
</bindingElementExtensions>
|
||||||
<bindingExtensions>
|
<bindingExtensions>
|
||||||
<add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</bindingExtensions>
|
</bindingExtensions>
|
||||||
</extensions>
|
</extensions>
|
||||||
</system.serviceModel>
|
</system.serviceModel>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<!-- Service Bus specific app setings for messaging connections -->
|
<!-- Service Bus specific app setings for messaging connections -->
|
||||||
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://[your namespace].servicebus.windows.net;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=[your secret]"/>
|
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://[your namespace].servicebus.windows.net;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=[your secret]" />
|
||||||
</appSettings>
|
</appSettings>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -119,7 +119,10 @@
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
|
|
@ -1,52 +1,56 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<connectionStrings>
|
<connectionStrings>
|
||||||
<add name="TestConnection1" connectionString="Test Connection 1"/>
|
<add name="TestConnection1" connectionString="Test Connection 1" />
|
||||||
<add name="TestConnection2" connectionString="Test Connection 2"/>
|
<add name="TestConnection2" connectionString="Test Connection 2" />
|
||||||
</connectionStrings>
|
</connectionStrings>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234"/>
|
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral"/>
|
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||||
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -1,49 +1,53 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<add key="Disable_DisabledJob" value="1"/>
|
<add key="Disable_DisabledJob" value="1" />
|
||||||
</appSettings>
|
</appSettings>
|
||||||
<connectionStrings>
|
<connectionStrings>
|
||||||
<!-- CS format: DefaultEndpointsProtocol=https;AccountName=[ACCOUNT];AccountKey=[KEY] -->
|
<!-- CS format: DefaultEndpointsProtocol=https;AccountName=[ACCOUNT];AccountKey=[KEY] -->
|
||||||
<add name="AzureWebJobsDashboard" connectionString=""/>
|
<add name="AzureWebJobsDashboard" connectionString="" />
|
||||||
<add name="AzureWebJobsStorage" connectionString=""/>
|
<add name="AzureWebJobsStorage" connectionString="" />
|
||||||
<add name="AzureWebJobsSecondaryStorage" connectionString=""/>
|
<add name="AzureWebJobsSecondaryStorage" connectionString="" />
|
||||||
|
|
||||||
<add name="AzureWebJobsServiceBus" connectionString=""/>
|
<add name="AzureWebJobsServiceBus" connectionString="" />
|
||||||
</connectionStrings>
|
</connectionStrings>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="xunit.core" publicKeyToken="8d05b1bb7a6fdb6c" culture="neutral"/>
|
<assemblyIdentity name="xunit.core" publicKeyToken="8d05b1bb7a6fdb6c" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.2929" newVersion="2.0.0.2929"/>
|
<bindingRedirect oldVersion="0.0.0.0-2.0.0.2929" newVersion="2.0.0.2929" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
@ -51,26 +55,26 @@
|
||||||
<extensions>
|
<extensions>
|
||||||
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
|
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
|
||||||
<behaviorExtensions>
|
<behaviorExtensions>
|
||||||
<add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</behaviorExtensions>
|
</behaviorExtensions>
|
||||||
<bindingElementExtensions>
|
<bindingElementExtensions>
|
||||||
<add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</bindingElementExtensions>
|
</bindingElementExtensions>
|
||||||
<bindingExtensions>
|
<bindingExtensions>
|
||||||
<add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
<add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
<add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
</bindingExtensions>
|
</bindingExtensions>
|
||||||
</extensions>
|
</extensions>
|
||||||
</system.serviceModel>
|
</system.serviceModel>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -12,10 +12,12 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
using Microsoft.Azure.WebJobs.Host.Queues;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
@ -51,6 +53,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
private readonly CloudQueue _testQueue;
|
private readonly CloudQueue _testQueue;
|
||||||
private readonly TestFixture _fixture;
|
private readonly TestFixture _fixture;
|
||||||
|
|
||||||
|
private readonly TestLoggerProvider _loggerProvider = new TestLoggerProvider();
|
||||||
|
|
||||||
public AsyncChainEndToEndTests(TestFixture fixture)
|
public AsyncChainEndToEndTests(TestFixture fixture)
|
||||||
{
|
{
|
||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
|
@ -66,6 +70,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
_storageAccount = fixture.StorageAccount;
|
_storageAccount = fixture.StorageAccount;
|
||||||
_timeoutJobDelay = TimeSpan.FromMinutes(5);
|
_timeoutJobDelay = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
|
ILoggerFactory loggerFactory = new LoggerFactory();
|
||||||
|
loggerFactory.AddProvider(_loggerProvider);
|
||||||
|
_hostConfig.LoggerFactory = loggerFactory;
|
||||||
|
_hostConfig.Aggregator.IsEnabled = false; // makes validation easier
|
||||||
|
|
||||||
CloudQueueClient queueClient = _storageAccount.CreateCloudQueueClient();
|
CloudQueueClient queueClient = _storageAccount.CreateCloudQueueClient();
|
||||||
string queueName = _resolver.ResolveInString(TestQueueName);
|
string queueName = _resolver.ResolveInString(TestQueueName);
|
||||||
_testQueue = queueClient.GetQueueReference(queueName);
|
_testQueue = queueClient.GetQueueReference(queueName);
|
||||||
|
@ -86,6 +95,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
|
|
||||||
await AsyncChainEndToEndInternal();
|
await AsyncChainEndToEndInternal();
|
||||||
|
|
||||||
|
Console.SetOut(hold);
|
||||||
|
|
||||||
string firstQueueName = _resolver.ResolveInString(Queue1Name);
|
string firstQueueName = _resolver.ResolveInString(Queue1Name);
|
||||||
string secondQueueName = _resolver.ResolveInString(Queue2Name);
|
string secondQueueName = _resolver.ResolveInString(Queue2Name);
|
||||||
string blobContainerName = _resolver.ResolveInString(ContainerName);
|
string blobContainerName = _resolver.ResolveInString(ContainerName);
|
||||||
|
@ -124,15 +135,23 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
}.OrderBy(p => p).ToArray();
|
}.OrderBy(p => p).ToArray();
|
||||||
|
|
||||||
bool hasError = consoleOutputLines.Any(p => p.Contains("Function had errors"));
|
bool hasError = consoleOutputLines.Any(p => p.Contains("Function had errors"));
|
||||||
if (!hasError)
|
Assert.False(hasError);
|
||||||
|
|
||||||
|
// Validate console output
|
||||||
|
for (int i = 0; i < expectedOutputLines.Length; i++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < expectedOutputLines.Length; i++)
|
Assert.StartsWith(expectedOutputLines[i], consoleOutputLines[i]);
|
||||||
{
|
|
||||||
Assert.StartsWith(expectedOutputLines[i], consoleOutputLines[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.SetOut(hold);
|
// Validate Logger output
|
||||||
|
var allLogs = _loggerProvider.CreatedLoggers.SelectMany(l => l.LogMessages.SelectMany(m => m.FormattedMessage.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None))).OrderBy(p => p).ToArray();
|
||||||
|
// Logger doesn't log the 'Executing' messages
|
||||||
|
var loggerExpected = expectedOutputLines.Where(l => !l.StartsWith("Executing '")).ToArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < loggerExpected.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.StartsWith(loggerExpected[i], allLogs[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,23 +207,131 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
await host.StopAsync();
|
await host.StopAsync();
|
||||||
|
|
||||||
bool hasError = string.Join(Environment.NewLine, trace.Traces.Where(p => p.Message.Contains("Error"))).Any();
|
bool hasError = string.Join(Environment.NewLine, trace.Traces.Where(p => p.Message.Contains("Error"))).Any();
|
||||||
if (!hasError)
|
Assert.False(hasError);
|
||||||
{
|
|
||||||
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("User TraceWriter log")));
|
|
||||||
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("User TextWriter log (TestParam)")));
|
|
||||||
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("Another User TextWriter log")));
|
|
||||||
ValidateTraceProperties(trace);
|
|
||||||
|
|
||||||
string[] consoleOutputLines = consoleOutput.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("User TraceWriter log")));
|
||||||
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("User TraceWriter log")));
|
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("User TextWriter log (TestParam)")));
|
||||||
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("User TextWriter log (TestParam)")));
|
Assert.NotNull(trace.Traces.SingleOrDefault(p => p.Message.Contains("Another User TextWriter log")));
|
||||||
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("Another User TextWriter log")));
|
ValidateTraceProperties(trace);
|
||||||
}
|
|
||||||
|
string[] consoleOutputLines = consoleOutput.ToString().Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||||
|
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("User TraceWriter log")));
|
||||||
|
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("User TextWriter log (TestParam)")));
|
||||||
|
Assert.NotNull(consoleOutputLines.SingleOrDefault(p => p.Contains("Another User TextWriter log")));
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Function).Single();
|
||||||
|
Assert.Equal(3, logger.LogMessages.Count);
|
||||||
|
Assert.NotNull(logger.LogMessages.SingleOrDefault(p => p.FormattedMessage.Contains("User TraceWriter log")));
|
||||||
|
Assert.NotNull(logger.LogMessages.SingleOrDefault(p => p.FormattedMessage.Contains("User TextWriter log (TestParam)")));
|
||||||
|
Assert.NotNull(logger.LogMessages.SingleOrDefault(p => p.FormattedMessage.Contains("Another User TextWriter log")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.SetOut(hold);
|
Console.SetOut(hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AggregatorAndEventCollector()
|
||||||
|
{
|
||||||
|
using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
|
||||||
|
{
|
||||||
|
_hostConfig.Tracing.ConsoleLevel = TraceLevel.Off;
|
||||||
|
|
||||||
|
// enable the aggregator
|
||||||
|
_hostConfig.Aggregator.IsEnabled = true;
|
||||||
|
_hostConfig.Aggregator.BatchSize = 1;
|
||||||
|
|
||||||
|
// add a FunctionEventCollector
|
||||||
|
var eventCollector = new TestFunctionEventCollector();
|
||||||
|
_hostConfig.AddService<IAsyncCollector<FunctionInstanceLogEntry>>(eventCollector);
|
||||||
|
|
||||||
|
JobHost host = new JobHost(_hostConfig);
|
||||||
|
|
||||||
|
await host.StartAsync();
|
||||||
|
await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));
|
||||||
|
|
||||||
|
_functionCompletedEvent.WaitOne();
|
||||||
|
|
||||||
|
// ensure all logs have had a chance to flush
|
||||||
|
await Task.Delay(3000);
|
||||||
|
|
||||||
|
await host.StopAsync();
|
||||||
|
|
||||||
|
// Make sure the aggregator was logged to
|
||||||
|
var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).Single();
|
||||||
|
Assert.Equal(4, logger.LogMessages.Count);
|
||||||
|
|
||||||
|
// Make sure the eventCollector was logged to
|
||||||
|
// The aggregator ignores 'start' evetns, so this will be double
|
||||||
|
Assert.Equal(8, eventCollector.LogCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AggregatorOnly()
|
||||||
|
{
|
||||||
|
using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
|
||||||
|
{
|
||||||
|
_hostConfig.Tracing.ConsoleLevel = TraceLevel.Off;
|
||||||
|
|
||||||
|
// enable the aggregator
|
||||||
|
_hostConfig.Aggregator.IsEnabled = true;
|
||||||
|
_hostConfig.Aggregator.BatchSize = 1;
|
||||||
|
|
||||||
|
JobHost host = new JobHost(_hostConfig);
|
||||||
|
|
||||||
|
await host.StartAsync();
|
||||||
|
await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));
|
||||||
|
|
||||||
|
_functionCompletedEvent.WaitOne();
|
||||||
|
|
||||||
|
// ensure all logs have had a chance to flush
|
||||||
|
await Task.Delay(3000);
|
||||||
|
|
||||||
|
await host.StopAsync();
|
||||||
|
|
||||||
|
// Make sure the aggregator was logged to
|
||||||
|
var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).Single();
|
||||||
|
Assert.Equal(4, logger.LogMessages.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EventCollectorOnly()
|
||||||
|
{
|
||||||
|
using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
|
||||||
|
{
|
||||||
|
_hostConfig.Tracing.ConsoleLevel = TraceLevel.Off;
|
||||||
|
|
||||||
|
// disable the aggregator
|
||||||
|
_hostConfig.Aggregator.IsEnabled = false;
|
||||||
|
|
||||||
|
// add a FunctionEventCollector
|
||||||
|
var eventCollector = new TestFunctionEventCollector();
|
||||||
|
_hostConfig.AddService<IAsyncCollector<FunctionInstanceLogEntry>>(eventCollector);
|
||||||
|
|
||||||
|
JobHost host = new JobHost(_hostConfig);
|
||||||
|
|
||||||
|
await host.StartAsync();
|
||||||
|
await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));
|
||||||
|
|
||||||
|
_functionCompletedEvent.WaitOne();
|
||||||
|
|
||||||
|
// ensure all logs have had a chance to flush
|
||||||
|
await Task.Delay(3000);
|
||||||
|
|
||||||
|
await host.StopAsync();
|
||||||
|
|
||||||
|
// Make sure the aggregator was logged to
|
||||||
|
var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).SingleOrDefault();
|
||||||
|
Assert.Null(logger);
|
||||||
|
|
||||||
|
// Make sure the eventCollector was logged to
|
||||||
|
// The aggregator ignores 'start' evetns, so this will be double
|
||||||
|
Assert.Equal(8, eventCollector.LogCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ValidateTraceProperties(TestTraceWriter trace)
|
private void ValidateTraceProperties(TestTraceWriter trace)
|
||||||
{
|
{
|
||||||
foreach (var traceEvent in trace.Traces)
|
foreach (var traceEvent in trace.Traces)
|
||||||
|
@ -251,17 +378,28 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
|
string expectedName = $"{methodInfo.DeclaringType.FullName}.{methodInfo.Name}";
|
||||||
|
|
||||||
|
// Validate TraceWriter
|
||||||
// We expect 3 error messages total
|
// We expect 3 error messages total
|
||||||
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
||||||
Assert.Equal(3, traceErrors.Length);
|
Assert.Equal(3, traceErrors.Length);
|
||||||
|
|
||||||
// Ensure that all errors include the same exception, with function
|
// Ensure that all errors include the same exception, with function
|
||||||
// invocation details
|
// invocation details
|
||||||
FunctionInvocationException functionException = traceErrors.First().Exception as FunctionInvocationException;
|
FunctionInvocationException functionException = traceErrors.First().Exception as FunctionInvocationException;
|
||||||
Assert.NotNull(functionException);
|
Assert.NotNull(functionException);
|
||||||
Assert.NotEqual(Guid.Empty, functionException.InstanceId);
|
Assert.NotEqual(Guid.Empty, functionException.InstanceId);
|
||||||
Assert.Equal(string.Format("{0}.{1}", methodInfo.DeclaringType.FullName, methodInfo.Name), functionException.MethodName);
|
Assert.Equal(expectedName, functionException.MethodName);
|
||||||
Assert.True(traceErrors.All(p => functionException == p.Exception));
|
Assert.True(traceErrors.All(p => functionException == p.Exception));
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
// Logger only writes out a single log message (which includes the Exception).
|
||||||
|
var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Results).Single();
|
||||||
|
var logMessage = logger.LogMessages.Single();
|
||||||
|
var loggerException = logMessage.Exception as FunctionException;
|
||||||
|
Assert.NotNull(loggerException);
|
||||||
|
Assert.Equal(expectedName, loggerException.MethodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -349,12 +487,24 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
host.Stop();
|
host.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string expectedExceptionMessage = $"Timeout value of 00:00:01 exceeded by function 'AsyncChainEndToEndTests.{functionName}'";
|
||||||
|
string expectedResultMessage = $"Executed 'AsyncChainEndToEndTests.{functionName}' (Failed, Id=";
|
||||||
|
|
||||||
|
// Validate TraceWriter
|
||||||
// We expect 3 error messages total
|
// We expect 3 error messages total
|
||||||
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
||||||
Assert.Equal(3, traceErrors.Length);
|
Assert.Equal(3, traceErrors.Length);
|
||||||
Assert.True(traceErrors[0].Message.StartsWith(string.Format("Timeout value of 00:00:01 exceeded by function 'AsyncChainEndToEndTests.{0}'", functionName)));
|
Assert.StartsWith(expectedExceptionMessage, traceErrors[0].Message);
|
||||||
Assert.True(traceErrors[1].Message.StartsWith(string.Format("Executed 'AsyncChainEndToEndTests.{0}' (Failed, Id=", functionName)));
|
Assert.StartsWith(expectedResultMessage, traceErrors[1].Message);
|
||||||
Assert.True(traceErrors[2].Message.Trim().StartsWith("Function had errors. See Azure WebJobs SDK dashboard for details."));
|
Assert.StartsWith("Function had errors. See Azure WebJobs SDK dashboard for details.", traceErrors[2].Message.Trim());
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
// One error is logged by the Executor and one as a Result.
|
||||||
|
var resultLogger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Results).Single();
|
||||||
|
var executorLogger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Executor).Single();
|
||||||
|
Assert.NotNull(resultLogger.LogMessages.Single().Exception);
|
||||||
|
Assert.StartsWith(expectedResultMessage, resultLogger.LogMessages.Single().FormattedMessage);
|
||||||
|
Assert.StartsWith(expectedExceptionMessage, executorLogger.LogMessages.Single().FormattedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -368,8 +518,13 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
MethodInfo methodInfo = GetType().GetMethod("TimeoutJob");
|
MethodInfo methodInfo = GetType().GetMethod("TimeoutJob");
|
||||||
await host.CallAsync(methodInfo);
|
await host.CallAsync(methodInfo);
|
||||||
|
|
||||||
|
// Validate TraceWriter
|
||||||
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
|
||||||
Assert.Equal(0, traceErrors.Length);
|
Assert.Equal(0, traceErrors.Length);
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
LogMessage[] logErrors = _loggerProvider.GetAllLogMessages().Where(l => l.Level == Extensions.Logging.LogLevel.Error).ToArray();
|
||||||
|
Assert.Equal(0, logErrors.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -781,5 +936,21 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestFunctionEventCollector : IAsyncCollector<FunctionInstanceLogEntry>
|
||||||
|
{
|
||||||
|
public int LogCount = 0;
|
||||||
|
|
||||||
|
public Task AddAsync(FunctionInstanceLogEntry item, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
LogCount++;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Microsoft.WindowsAzure.Storage.Blob;
|
using Microsoft.WindowsAzure.Storage.Blob;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
|
@ -335,6 +336,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
var tracer = new TestTraceWriter(TraceLevel.Verbose);
|
var tracer = new TestTraceWriter(TraceLevel.Verbose);
|
||||||
hostConfig.Tracing.Tracers.Add(tracer);
|
hostConfig.Tracing.Tracers.Add(tracer);
|
||||||
|
|
||||||
|
ILoggerFactory loggerFactory = new LoggerFactory();
|
||||||
|
TestLoggerProvider loggerProvider = new TestLoggerProvider();
|
||||||
|
loggerFactory.AddProvider(loggerProvider);
|
||||||
|
hostConfig.LoggerFactory = loggerFactory;
|
||||||
|
|
||||||
// The jobs host is started
|
// The jobs host is started
|
||||||
JobHost host = new JobHost(hostConfig);
|
JobHost host = new JobHost(hostConfig);
|
||||||
host.Start();
|
host.Start();
|
||||||
|
@ -385,6 +391,10 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
|
||||||
// make sure the exception is being properly logged
|
// make sure the exception is being properly logged
|
||||||
var errors = tracer.Traces.Where(t => t.Level == TraceLevel.Error);
|
var errors = tracer.Traces.Where(t => t.Level == TraceLevel.Error);
|
||||||
Assert.True(errors.All(t => t.Exception.InnerException.InnerException is FormatException));
|
Assert.True(errors.All(t => t.Exception.InnerException.InnerException is FormatException));
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var loggerErrors = loggerProvider.GetAllLogMessages().Where(l => l.Level == Extensions.Logging.LogLevel.Error);
|
||||||
|
Assert.True(loggerErrors.All(t => t.Exception.InnerException.InnerException is FormatException));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UploadTestObject()
|
private void UploadTestObject()
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<CodeAnalysisRuleSet>..\test.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
@ -54,10 +57,26 @@
|
||||||
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.ServiceBus, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.ServiceBus, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\WindowsAzure.ServiceBus.3.4.5\lib\net45-full\Microsoft.ServiceBus.dll</HintPath>
|
<HintPath>..\..\packages\WindowsAzure.ServiceBus.3.4.5\lib\net45-full\Microsoft.ServiceBus.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -71,9 +90,72 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Runtime.Serialization" />
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.ServiceModel" />
|
<Reference Include="System.ServiceModel" />
|
||||||
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
||||||
|
@ -81,6 +163,10 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
|
|
@ -4,13 +4,63 @@
|
||||||
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
||||||
|
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ComponentModel" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Http" version="4.3.1" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ObjectModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="WindowsAzure.ServiceBus" version="3.4.5" targetFramework="net45" />
|
<package id="WindowsAzure.ServiceBus" version="3.4.5" targetFramework="net45" />
|
||||||
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
||||||
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.FunctionalTests.TestDoubles;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
|
||||||
|
{
|
||||||
|
public class ILoggerTests
|
||||||
|
{
|
||||||
|
TestTraceWriter _trace = new TestTraceWriter(TraceLevel.Info);
|
||||||
|
TestLoggerProvider _loggerProvider = new TestLoggerProvider();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ILogger_Succeeds()
|
||||||
|
{
|
||||||
|
using (JobHost host = new JobHost(CreateConfig()))
|
||||||
|
{
|
||||||
|
var method = typeof(ILoggerFunctions).GetMethod(nameof(ILoggerFunctions.ILogger));
|
||||||
|
host.Call(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Five loggers are the startup, singleton, executor, results, and function loggers
|
||||||
|
Assert.Equal(5, _loggerProvider.CreatedLoggers.Count);
|
||||||
|
|
||||||
|
var functionLogger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Function).Single();
|
||||||
|
var resultsLogger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Results).Single();
|
||||||
|
|
||||||
|
Assert.Equal(2, functionLogger.LogMessages.Count);
|
||||||
|
var infoMessage = functionLogger.LogMessages[0];
|
||||||
|
var errorMessage = functionLogger.LogMessages[1];
|
||||||
|
// These get the {OriginalFormat} property as well as the 3 from TraceWriter
|
||||||
|
Assert.Equal(3, infoMessage.State.Count());
|
||||||
|
Assert.Equal(3, errorMessage.State.Count());
|
||||||
|
|
||||||
|
Assert.Equal(1, resultsLogger.LogMessages.Count);
|
||||||
|
//TODO: beef these verifications up
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TraceWriter_ForwardsTo_ILogger()
|
||||||
|
{
|
||||||
|
using (JobHost host = new JobHost(CreateConfig()))
|
||||||
|
{
|
||||||
|
var method = typeof(ILoggerFunctions).GetMethod(nameof(ILoggerFunctions.TraceWriterWithILoggerFactory));
|
||||||
|
host.Call(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(5, _trace.Traces.Count);
|
||||||
|
// The third and fourth traces are from our function
|
||||||
|
var infoLog = _trace.Traces[2];
|
||||||
|
var errorLog = _trace.Traces[3];
|
||||||
|
|
||||||
|
Assert.Equal("This should go to the ILogger", infoLog.Message);
|
||||||
|
Assert.Null(infoLog.Exception);
|
||||||
|
Assert.Equal(3, infoLog.Properties.Count);
|
||||||
|
|
||||||
|
Assert.Equal("This should go to the ILogger with an Exception!", errorLog.Message);
|
||||||
|
Assert.IsType<InvalidOperationException>(errorLog.Exception);
|
||||||
|
Assert.Equal(3, errorLog.Properties.Count);
|
||||||
|
|
||||||
|
// Five loggers are the startup, singleton, executor, results, and function loggers
|
||||||
|
Assert.Equal(5, _loggerProvider.CreatedLoggers.Count);
|
||||||
|
var functionLogger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Function).Single();
|
||||||
|
Assert.Equal(2, functionLogger.LogMessages.Count);
|
||||||
|
var infoMessage = functionLogger.LogMessages[0];
|
||||||
|
var errorMessage = functionLogger.LogMessages[1];
|
||||||
|
// These get the {OriginalFormat} property as well as the 3 from TraceWriter
|
||||||
|
Assert.Equal(4, infoMessage.State.Count());
|
||||||
|
Assert.Equal(4, errorMessage.State.Count());
|
||||||
|
//TODO: beef these verifications up
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Aggregator_Runs_WhenEnabled_AndFlushes_OnStop()
|
||||||
|
{
|
||||||
|
int addCalls = 0;
|
||||||
|
int flushCalls = 0;
|
||||||
|
|
||||||
|
var config = CreateConfig();
|
||||||
|
|
||||||
|
var mockAggregator = new Mock<IAsyncCollector<FunctionInstanceLogEntry>>(MockBehavior.Strict);
|
||||||
|
mockAggregator
|
||||||
|
.Setup(a => a.AddAsync(It.IsAny<FunctionInstanceLogEntry>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Callback<FunctionInstanceLogEntry, CancellationToken>((l, t) => addCalls++)
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
mockAggregator
|
||||||
|
.Setup(a => a.FlushAsync(It.IsAny<CancellationToken>()))
|
||||||
|
.Callback<CancellationToken>(t => flushCalls++)
|
||||||
|
.Returns(Task.CompletedTask);
|
||||||
|
|
||||||
|
var mockFactory = new Mock<IFunctionResultAggregatorFactory>(MockBehavior.Strict);
|
||||||
|
mockFactory
|
||||||
|
.Setup(f => f.Create(5, TimeSpan.FromSeconds(1), It.IsAny<ILoggerFactory>()))
|
||||||
|
.Returns(mockAggregator.Object);
|
||||||
|
|
||||||
|
config.AddService<IFunctionResultAggregatorFactory>(mockFactory.Object);
|
||||||
|
|
||||||
|
config.Aggregator.IsEnabled = true;
|
||||||
|
config.Aggregator.BatchSize = 5;
|
||||||
|
config.Aggregator.FlushTimeout = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
using (JobHost host = new JobHost(config))
|
||||||
|
{
|
||||||
|
host.Start();
|
||||||
|
|
||||||
|
var method = typeof(ILoggerFunctions).GetMethod(nameof(ILoggerFunctions.TraceWriterWithILoggerFactory));
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
host.Call(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
host.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add will be called 5 times. The default aggregator will ingore the
|
||||||
|
// 'Function started' calls.
|
||||||
|
Assert.Equal(10, addCalls);
|
||||||
|
|
||||||
|
// Flush is called on host stop
|
||||||
|
Assert.Equal(1, flushCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoILoggerFactory_NoAggregator()
|
||||||
|
{
|
||||||
|
var config = CreateConfig(addFactory: false);
|
||||||
|
|
||||||
|
// Ensure the aggregator is never configured by registering an
|
||||||
|
// AggregatorFactory that with a strict, unconfigured mock.
|
||||||
|
var mockFactory = new Mock<IFunctionResultAggregatorFactory>(MockBehavior.Strict);
|
||||||
|
config.AddService<IFunctionResultAggregatorFactory>(mockFactory.Object);
|
||||||
|
|
||||||
|
using (JobHost host = new JobHost(config))
|
||||||
|
{
|
||||||
|
var method = typeof(ILoggerFunctions).GetMethod(nameof(ILoggerFunctions.TraceWriterWithILoggerFactory));
|
||||||
|
host.Call(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DisabledAggregator_NoAggregator()
|
||||||
|
{
|
||||||
|
// Add the loggerfactory but disable the aggregator
|
||||||
|
var config = CreateConfig();
|
||||||
|
config.Aggregator.IsEnabled = false;
|
||||||
|
|
||||||
|
// Ensure the aggregator is never configured by registering an
|
||||||
|
// AggregatorFactory that with a strict, unconfigured mock.
|
||||||
|
var mockFactory = new Mock<IFunctionResultAggregatorFactory>(MockBehavior.Strict);
|
||||||
|
config.AddService<IFunctionResultAggregatorFactory>(mockFactory.Object);
|
||||||
|
|
||||||
|
using (JobHost host = new JobHost(config))
|
||||||
|
{
|
||||||
|
// also start and stop the host to ensure nothing throws due to the
|
||||||
|
// null aggregator
|
||||||
|
host.Start();
|
||||||
|
|
||||||
|
var method = typeof(ILoggerFunctions).GetMethod(nameof(ILoggerFunctions.TraceWriterWithILoggerFactory));
|
||||||
|
host.Call(method);
|
||||||
|
|
||||||
|
host.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JobHostConfiguration CreateConfig(bool addFactory = true)
|
||||||
|
{
|
||||||
|
IStorageAccountProvider accountProvider = new FakeStorageAccountProvider()
|
||||||
|
{
|
||||||
|
StorageAccount = new FakeStorageAccount()
|
||||||
|
};
|
||||||
|
|
||||||
|
ILoggerFactory factory = new LoggerFactory();
|
||||||
|
factory.AddProvider(_loggerProvider);
|
||||||
|
|
||||||
|
var config = new JobHostConfiguration();
|
||||||
|
config.AddService(accountProvider);
|
||||||
|
config.TypeLocator = new FakeTypeLocator(new[] { typeof(ILoggerFunctions) });
|
||||||
|
config.Tracing.Tracers.Add(_trace);
|
||||||
|
config.AddService(factory);
|
||||||
|
config.Aggregator.IsEnabled = false; // disable aggregator
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ILoggerFunctions
|
||||||
|
{
|
||||||
|
[NoAutomaticTrigger]
|
||||||
|
public void ILogger(ILogger log)
|
||||||
|
{
|
||||||
|
log.LogInformation("Log {some} keys and {values}", "1", "2");
|
||||||
|
|
||||||
|
var ex = new InvalidOperationException("Failure.");
|
||||||
|
log.LogError(0, ex, "Log {other} keys {and} values", "3", "4");
|
||||||
|
}
|
||||||
|
|
||||||
|
[NoAutomaticTrigger]
|
||||||
|
public void TraceWriterWithILoggerFactory(TraceWriter log)
|
||||||
|
{
|
||||||
|
log.Info("This should go to the ILogger");
|
||||||
|
|
||||||
|
var ex = new InvalidOperationException("Failure.");
|
||||||
|
log.Error("This should go to the ILogger with an Exception!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
using Microsoft.Azure.WebJobs.Host.Queues;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage.Queue;
|
using Microsoft.WindowsAzure.Storage.Queue;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -31,7 +32,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
|
||||||
_poisonQueue = fixture.PoisonQueue;
|
_poisonQueue = fixture.PoisonQueue;
|
||||||
|
|
||||||
_queuesConfig = new JobHostQueuesConfiguration();
|
_queuesConfig = new JobHostQueuesConfiguration();
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, _queuesConfig);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, _queuesConfig);
|
||||||
_processor = new QueueProcessor(context);
|
_processor = new QueueProcessor(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
|
||||||
VisibilityTimeout = TimeSpan.FromSeconds(30),
|
VisibilityTimeout = TimeSpan.FromSeconds(30),
|
||||||
MaxPollingInterval = TimeSpan.FromSeconds(15)
|
MaxPollingInterval = TimeSpan.FromSeconds(15)
|
||||||
};
|
};
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, config);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, config);
|
||||||
QueueProcessor localProcessor = new QueueProcessor(context);
|
QueueProcessor localProcessor = new QueueProcessor(context);
|
||||||
|
|
||||||
Assert.Equal(config.BatchSize, localProcessor.BatchSize);
|
Assert.Equal(config.BatchSize, localProcessor.BatchSize);
|
||||||
|
@ -94,7 +95,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CompleteProcessingMessageAsync_MaxDequeueCountExceeded_MovesMessageToPoisonQueue()
|
public async Task CompleteProcessingMessageAsync_MaxDequeueCountExceeded_MovesMessageToPoisonQueue()
|
||||||
{
|
{
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, _queuesConfig, _poisonQueue);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, _queuesConfig, _poisonQueue);
|
||||||
QueueProcessor localProcessor = new QueueProcessor(context);
|
QueueProcessor localProcessor = new QueueProcessor(context);
|
||||||
|
|
||||||
bool poisonMessageHandlerCalled = false;
|
bool poisonMessageHandlerCalled = false;
|
||||||
|
@ -134,7 +135,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
|
||||||
// configure a non-zero visibility timeout
|
// configure a non-zero visibility timeout
|
||||||
VisibilityTimeout = TimeSpan.FromMinutes(5)
|
VisibilityTimeout = TimeSpan.FromMinutes(5)
|
||||||
};
|
};
|
||||||
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, queuesConfig, _poisonQueue);
|
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, queuesConfig, _poisonQueue);
|
||||||
QueueProcessor localProcessor = new QueueProcessor(context);
|
QueueProcessor localProcessor = new QueueProcessor(context);
|
||||||
|
|
||||||
string messageContent = Guid.NewGuid().ToString();
|
string messageContent = Guid.NewGuid().ToString();
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
<Compile Include="Blobs\Listeners\ScanContainersStrategyTests.cs" />
|
<Compile Include="Blobs\Listeners\ScanContainersStrategyTests.cs" />
|
||||||
<Compile Include="Blobs\Listeners\StorageBlobScanInfoManagerTests.cs" />
|
<Compile Include="Blobs\Listeners\StorageBlobScanInfoManagerTests.cs" />
|
||||||
<Compile Include="BlobTriggerTests.cs" />
|
<Compile Include="BlobTriggerTests.cs" />
|
||||||
|
<Compile Include="ILoggerTests.cs" />
|
||||||
<Compile Include="JobHostTests.cs" />
|
<Compile Include="JobHostTests.cs" />
|
||||||
<Compile Include="FunctionalTest.cs" />
|
<Compile Include="FunctionalTest.cs" />
|
||||||
<Compile Include="BlobTests.cs" />
|
<Compile Include="BlobTests.cs" />
|
||||||
|
@ -145,6 +146,22 @@
|
||||||
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -162,14 +179,81 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
</appSettings>
|
</appSettings>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -5,14 +5,64 @@
|
||||||
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
||||||
<package id="Moq" version="4.5.30" targetFramework="net45" />
|
<package id="Moq" version="4.5.30" targetFramework="net45" />
|
||||||
|
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ComponentModel" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Http" version="4.3.1" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ObjectModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
||||||
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
||||||
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
|
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
|
||||||
|
|
|
@ -1,15 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Blobs;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
|
{
|
||||||
|
public class LogMessage
|
||||||
|
{
|
||||||
|
public LogLevel Level { get; set; }
|
||||||
|
public EventId EventId { get; set; }
|
||||||
|
public IEnumerable<KeyValuePair<string, object>> State { get; set; }
|
||||||
|
public Exception Exception { get; set; }
|
||||||
|
public string FormattedMessage { get; set; }
|
||||||
|
|
||||||
|
public string Category { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
{
|
{
|
||||||
|
@ -47,7 +48,7 @@ namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
return new NullFunctionOutput();
|
return new NullFunctionOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace)
|
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
|
{
|
||||||
|
public class TestLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly Func<string, LogLevel, bool> _filter;
|
||||||
|
|
||||||
|
public string Category { get; private set; }
|
||||||
|
|
||||||
|
public IList<LogMessage> LogMessages = new List<LogMessage>();
|
||||||
|
|
||||||
|
public TestLogger(string category, Func<string, LogLevel, bool> filter = null)
|
||||||
|
{
|
||||||
|
Category = category;
|
||||||
|
_filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
return _filter?.Invoke(Category, logLevel) ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
if (!IsEnabled(logLevel))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMessages.Add(new LogMessage
|
||||||
|
{
|
||||||
|
Level = logLevel,
|
||||||
|
EventId = eventId,
|
||||||
|
State = state as IEnumerable<KeyValuePair<string, object>>,
|
||||||
|
Exception = exception,
|
||||||
|
FormattedMessage = formatter(state, exception),
|
||||||
|
Category = Category
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.TestCommon
|
||||||
|
{
|
||||||
|
public class TestLoggerProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
private readonly Func<string, LogLevel, bool> _filter;
|
||||||
|
|
||||||
|
public IList<TestLogger> CreatedLoggers = new List<TestLogger>();
|
||||||
|
|
||||||
|
public TestLoggerProvider(Func<string, LogLevel, bool> filter = null)
|
||||||
|
{
|
||||||
|
_filter = filter ?? new LogCategoryFilter().Filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName)
|
||||||
|
{
|
||||||
|
var logger = new TestLogger(categoryName, _filter);
|
||||||
|
CreatedLoggers.Add(logger);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<LogMessage> GetAllLogMessages()
|
||||||
|
{
|
||||||
|
return CreatedLoggers.SelectMany(l => l.LogMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,22 @@
|
||||||
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -79,7 +95,70 @@
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
|
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.Compression.FileSystem" />
|
||||||
|
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Http.4.3.1\lib\net46\System.Net.Http.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Spatial, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -89,6 +168,10 @@
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -116,6 +199,7 @@
|
||||||
<Compile Include="FakeExtensionTypeLocator.cs" />
|
<Compile Include="FakeExtensionTypeLocator.cs" />
|
||||||
<Compile Include="FakeNameResolver.cs" />
|
<Compile Include="FakeNameResolver.cs" />
|
||||||
<Compile Include="InvariantCultureFixture.cs" />
|
<Compile Include="InvariantCultureFixture.cs" />
|
||||||
|
<Compile Include="LogMessage.cs" />
|
||||||
<Compile Include="NullConsoleProvider.cs" />
|
<Compile Include="NullConsoleProvider.cs" />
|
||||||
<Compile Include="NullFunctionInstanceLogger.cs" />
|
<Compile Include="NullFunctionInstanceLogger.cs" />
|
||||||
<Compile Include="NullFunctionOutputLoggerProvider.cs" />
|
<Compile Include="NullFunctionOutputLoggerProvider.cs" />
|
||||||
|
@ -123,6 +207,8 @@
|
||||||
<Compile Include="NullHostInstanceLoggerProvider.cs" />
|
<Compile Include="NullHostInstanceLoggerProvider.cs" />
|
||||||
<Compile Include="ScriptHelpers.cs" />
|
<Compile Include="ScriptHelpers.cs" />
|
||||||
<Compile Include="TestHelpers.cs" />
|
<Compile Include="TestHelpers.cs" />
|
||||||
|
<Compile Include="TestLogger.cs" />
|
||||||
|
<Compile Include="TestLoggerProvider.cs" />
|
||||||
<Compile Include="TestTraceWriter.cs" />
|
<Compile Include="TestTraceWriter.cs" />
|
||||||
<Compile Include="WebJobsShutdownContext.cs" />
|
<Compile Include="WebJobsShutdownContext.cs" />
|
||||||
<Compile Include="ExceptionAssert.cs" />
|
<Compile Include="ExceptionAssert.cs" />
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
|
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
|
||||||
|
|
|
@ -4,13 +4,63 @@
|
||||||
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net45" />
|
||||||
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Extensions.Logging.Abstractions" version="1.1.1" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net46" />
|
||||||
|
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.2.3" targetFramework="net45" />
|
||||||
|
<package id="NETStandard.Library" version="1.6.1" targetFramework="net46" />
|
||||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||||
|
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ComponentModel" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
<package id="System.ComponentModel.EventBasedAsync" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Console" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
<package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Globalization" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
<package id="System.Linq.Queryable" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Http" version="4.3.1" targetFramework="net46" />
|
||||||
|
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
<package id="System.Net.Requests" version="4.3.0" targetFramework="net45" />
|
||||||
|
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.ObjectModel" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
<package id="System.Spatial" version="5.8.2" targetFramework="net45" />
|
||||||
|
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||||
|
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||||
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
<package id="WindowsAzure.Storage" version="7.2.1" targetFramework="net45" />
|
||||||
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
<package id="xunit" version="2.1.0" targetFramework="net45" />
|
||||||
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
|
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
|
|
||||||
TimeoutAttribute attribute = method.GetCustomAttribute<TimeoutAttribute>();
|
TimeoutAttribute attribute = method.GetCustomAttribute<TimeoutAttribute>();
|
||||||
|
|
||||||
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter);
|
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter, null);
|
||||||
|
|
||||||
Assert.True(timer.Enabled);
|
Assert.True(timer.Enabled);
|
||||||
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
||||||
|
@ -64,7 +66,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
|
|
||||||
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
||||||
|
|
||||||
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter);
|
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter, null);
|
||||||
|
|
||||||
Assert.True(timer.Enabled);
|
Assert.True(timer.Enabled);
|
||||||
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
||||||
|
@ -76,7 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
public void StartFunctionTimeout_NoTimeout_ReturnsNull()
|
public void StartFunctionTimeout_NoTimeout_ReturnsNull()
|
||||||
{
|
{
|
||||||
TimeoutAttribute timeoutAttribute = null;
|
TimeoutAttribute timeoutAttribute = null;
|
||||||
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(null, timeoutAttribute, _cancellationTokenSource, _traceWriter);
|
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(null, timeoutAttribute, _cancellationTokenSource, _traceWriter, null);
|
||||||
|
|
||||||
Assert.Null(timer);
|
Assert.Null(timer);
|
||||||
}
|
}
|
||||||
|
@ -90,7 +92,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
||||||
attribute.ThrowOnTimeout = false;
|
attribute.ThrowOnTimeout = false;
|
||||||
|
|
||||||
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter);
|
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter, null);
|
||||||
|
|
||||||
Assert.Null(timer);
|
Assert.Null(timer);
|
||||||
|
|
||||||
|
@ -109,7 +111,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
|
|
||||||
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
|
||||||
|
|
||||||
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter);
|
System.Timers.Timer timer = FunctionExecutor.StartFunctionTimeout(_mockFunctionInstance.Object, attribute, _cancellationTokenSource, _traceWriter, null);
|
||||||
|
|
||||||
Assert.True(timer.Enabled);
|
Assert.True(timer.Enabled);
|
||||||
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
Assert.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
|
||||||
|
@ -317,16 +319,26 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
|
||||||
TimeoutAttribute attribute = method.GetCustomAttribute<TimeoutAttribute>();
|
TimeoutAttribute attribute = method.GetCustomAttribute<TimeoutAttribute>();
|
||||||
Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4");
|
Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4");
|
||||||
bool timeoutWhileDebugging = false;
|
bool timeoutWhileDebugging = false;
|
||||||
FunctionExecutor.OnFunctionTimeout(timer, method, instanceId, attribute.Timeout, timeoutWhileDebugging, _traceWriter, _cancellationTokenSource, () => isDebugging);
|
|
||||||
|
TestLogger logger = new TestLogger("Tests.FunctionExecutor");
|
||||||
|
|
||||||
|
FunctionExecutor.OnFunctionTimeout(timer, method, instanceId, attribute.Timeout, timeoutWhileDebugging, _traceWriter, logger, _cancellationTokenSource, () => isDebugging);
|
||||||
|
|
||||||
Assert.False(timer.Enabled);
|
Assert.False(timer.Enabled);
|
||||||
Assert.NotEqual(isDebugging, _cancellationTokenSource.IsCancellationRequested);
|
Assert.NotEqual(isDebugging, _cancellationTokenSource.IsCancellationRequested);
|
||||||
|
|
||||||
|
string message = string.Format("Timeout value of 00:01:00 exceeded by function 'Functions.MethodLevel' (Id: 'b2d1dd72-80e2-412b-a22e-3b4558f378b4'). {0}", expectedMessage);
|
||||||
|
|
||||||
|
// verify TraceWriter
|
||||||
TraceEvent trace = _traceWriter.Traces[0];
|
TraceEvent trace = _traceWriter.Traces[0];
|
||||||
Assert.Equal(TraceLevel.Error, trace.Level);
|
Assert.Equal(TraceLevel.Error, trace.Level);
|
||||||
Assert.Equal(TraceSource.Execution, trace.Source);
|
Assert.Equal(TraceSource.Execution, trace.Source);
|
||||||
string message = string.Format("Timeout value of 00:01:00 exceeded by function 'Functions.MethodLevel' (Id: 'b2d1dd72-80e2-412b-a22e-3b4558f378b4'). {0}", expectedMessage);
|
|
||||||
Assert.Equal(message, trace.Message);
|
Assert.Equal(message, trace.Message);
|
||||||
|
|
||||||
|
// verify ILogger
|
||||||
|
LogMessage log = logger.LogMessages.Single();
|
||||||
|
Assert.Equal(LogLevel.Error, log.Level);
|
||||||
|
Assert.Equal(message, log.FormattedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GlobalLevel(CancellationToken cancellationToken)
|
public static void GlobalLevel(CancellationToken cancellationToken)
|
||||||
|
|
|
@ -5,16 +5,14 @@ using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Blobs;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Loggers;
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Queues;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage;
|
using Microsoft.Azure.WebJobs.Host.Storage;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
using Microsoft.Azure.WebJobs.Host.Timers;
|
using Microsoft.Azure.WebJobs.Host.Timers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.WindowsAzure.Storage;
|
using Microsoft.WindowsAzure.Storage;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
||||||
|
@ -22,7 +20,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
|
||||||
{
|
{
|
||||||
internal static class FunctionIndexerFactory
|
internal static class FunctionIndexerFactory
|
||||||
{
|
{
|
||||||
public static FunctionIndexer Create(CloudStorageAccount account = null, INameResolver nameResolver = null, IExtensionRegistry extensionRegistry = null, TraceWriter traceWriter = null)
|
public static FunctionIndexer Create(CloudStorageAccount account = null, INameResolver nameResolver = null,
|
||||||
|
IExtensionRegistry extensionRegistry = null, TraceWriter traceWriter = null, ILoggerFactory loggerFactory = null)
|
||||||
{
|
{
|
||||||
IStorageAccountProvider storageAccountProvider = GetStorageAccountProvider(account);
|
IStorageAccountProvider storageAccountProvider = GetStorageAccountProvider(account);
|
||||||
|
|
||||||
|
@ -42,7 +41,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
|
||||||
|
|
||||||
IFunctionExecutor executor = new FunctionExecutor(new NullFunctionInstanceLogger(), outputLogger, exceptionHandler, logger);
|
IFunctionExecutor executor = new FunctionExecutor(new NullFunctionInstanceLogger(), outputLogger, exceptionHandler, logger);
|
||||||
|
|
||||||
return new FunctionIndexer(triggerBindingProvider, bindingProvider, DefaultJobActivator.Instance, executor, extensionRegistry, singletonManager, logger);
|
return new FunctionIndexer(triggerBindingProvider, bindingProvider, DefaultJobActivator.Instance, executor,
|
||||||
|
extensionRegistry, singletonManager, logger, loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IStorageAccountProvider GetStorageAccountProvider(CloudStorageAccount account)
|
private static IStorageAccountProvider GetStorageAccountProvider(CloudStorageAccount account)
|
||||||
|
|
|
@ -11,9 +11,9 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Config;
|
using Microsoft.Azure.WebJobs.Host.Config;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
|
||||||
Mock<IFunctionExecutor> executorMock = new Mock<IFunctionExecutor>(MockBehavior.Strict);
|
Mock<IFunctionExecutor> executorMock = new Mock<IFunctionExecutor>(MockBehavior.Strict);
|
||||||
|
|
||||||
IFunctionIndexCollector stubIndex = new Mock<IFunctionIndexCollector>().Object;
|
IFunctionIndexCollector stubIndex = new Mock<IFunctionIndexCollector>().Object;
|
||||||
|
|
||||||
FunctionIndexer indexer = new FunctionIndexer(
|
FunctionIndexer indexer = new FunctionIndexer(
|
||||||
new Mock<ITriggerBindingProvider>(MockBehavior.Strict).Object,
|
new Mock<ITriggerBindingProvider>(MockBehavior.Strict).Object,
|
||||||
new Mock<IBindingProvider>(MockBehavior.Strict).Object,
|
new Mock<IBindingProvider>(MockBehavior.Strict).Object,
|
||||||
|
@ -42,7 +43,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
|
||||||
executorMock.Object,
|
executorMock.Object,
|
||||||
extensionsMock.Object,
|
extensionsMock.Object,
|
||||||
new SingletonManager(),
|
new SingletonManager(),
|
||||||
new TestTraceWriter(TraceLevel.Verbose));
|
new TestTraceWriter(TraceLevel.Verbose),
|
||||||
|
null);
|
||||||
|
|
||||||
Assert.Throws<FunctionIndexingException>(() => indexer.IndexMethodAsync(method, stubIndex, CancellationToken.None).GetAwaiter().GetResult());
|
Assert.Throws<FunctionIndexingException>(() => indexer.IndexMethodAsync(method, stubIndex, CancellationToken.None).GetAwaiter().GetResult());
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,11 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Bindings;
|
using Microsoft.Azure.WebJobs.Host.Bindings;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Azure.WebJobs.Host.Triggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -151,16 +153,28 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
|
||||||
public async Task IndexMethod_IfMethodReturnsAsyncVoid_Throws()
|
public async Task IndexMethod_IfMethodReturnsAsyncVoid_Throws()
|
||||||
{
|
{
|
||||||
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
|
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
|
||||||
|
var loggerFactory = new LoggerFactory();
|
||||||
|
var loggerProvider = new TestLoggerProvider();
|
||||||
|
loggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
IFunctionIndexCollector index = CreateStubFunctionIndex();
|
IFunctionIndexCollector index = CreateStubFunctionIndex();
|
||||||
FunctionIndexer product = CreateProductUnderTest(traceWriter: traceWriter);
|
FunctionIndexer product = CreateProductUnderTest(traceWriter: traceWriter, loggerFactory: loggerFactory);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
await product.IndexMethodAsync(typeof(FunctionIndexerTests).GetMethod("ReturnAsyncVoid"), index, CancellationToken.None);
|
await product.IndexMethodAsync(typeof(FunctionIndexerTests).GetMethod("ReturnAsyncVoid"), index, CancellationToken.None);
|
||||||
|
|
||||||
var warning = traceWriter.Traces.First(p => p.Level == TraceLevel.Warning);
|
string expectedMessage = "Function 'ReturnAsyncVoid' is async but does not return a Task. Your function may not run correctly.";
|
||||||
Assert.Equal("Function 'ReturnAsyncVoid' is async but does not return a Task. Your function may not run correctly.", warning.Message);
|
|
||||||
|
// Validate TraceWriter
|
||||||
|
var traceWarning = traceWriter.Traces.First(p => p.Level == TraceLevel.Warning);
|
||||||
|
Assert.Equal(expectedMessage, traceWarning.Message);
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var logger = loggerProvider.CreatedLoggers.Single(l => l.Category == LogCategories.Startup);
|
||||||
|
var loggerWarning = logger.LogMessages.Single();
|
||||||
|
Assert.Equal(LogLevel.Warning, loggerWarning.Level);
|
||||||
|
Assert.Equal(expectedMessage, loggerWarning.FormattedMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -317,9 +331,9 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
|
||||||
return new Mock<IFunctionIndexCollector>(MockBehavior.Strict).Object;
|
return new Mock<IFunctionIndexCollector>(MockBehavior.Strict).Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FunctionIndexer CreateProductUnderTest(TraceWriter traceWriter = null)
|
private static FunctionIndexer CreateProductUnderTest(TraceWriter traceWriter = null, ILoggerFactory loggerFactory = null)
|
||||||
{
|
{
|
||||||
return FunctionIndexerFactory.Create(traceWriter: traceWriter);
|
return FunctionIndexerFactory.Create(traceWriter: traceWriter, loggerFactory: loggerFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IFunctionIndexCollector CreateStubFunctionIndex()
|
private static IFunctionIndexCollector CreateStubFunctionIndex()
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
using Microsoft.Azure.WebJobs.Host.Triggers;
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,16 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
ShortName = "testfunc"
|
ShortName = "testfunc"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
private readonly TestLoggerProvider _loggerProvider;
|
||||||
|
|
||||||
|
public FunctionListenerTests()
|
||||||
|
{
|
||||||
|
_loggerFactory = new LoggerFactory();
|
||||||
|
_loggerProvider = new TestLoggerProvider();
|
||||||
|
_loggerFactory.AddProvider(_loggerProvider);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FunctionListener_Throws_IfUnhandledListenerExceptionOnStartAsync()
|
public async Task FunctionListener_Throws_IfUnhandledListenerExceptionOnStartAsync()
|
||||||
{
|
{
|
||||||
|
@ -32,12 +42,20 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
||||||
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
||||||
.Throws(new Exception("listener"));
|
.Throws(new Exception("listener"));
|
||||||
var listener = new FunctionListener(badListener.Object, fd, trace);
|
var listener = new FunctionListener(badListener.Object, fd, trace, _loggerFactory);
|
||||||
|
|
||||||
var e = await Assert.ThrowsAsync<FunctionListenerException>(async () => await listener.StartAsync(ct));
|
var e = await Assert.ThrowsAsync<FunctionListenerException>(async () => await listener.StartAsync(ct));
|
||||||
var exc = trace.Traces[0].Exception as FunctionException;
|
|
||||||
Assert.Equal("testfunc", exc.MethodName);
|
// Validate TraceWriter
|
||||||
Assert.False(exc.Handled);
|
var traceEx = trace.Traces[0].Exception as FunctionException;
|
||||||
|
Assert.Equal("testfunc", traceEx.MethodName);
|
||||||
|
Assert.False(traceEx.Handled);
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var loggerEx = _loggerProvider.CreatedLoggers.Single().LogMessages.Single().Exception as FunctionException;
|
||||||
|
Assert.Equal("testfunc", loggerEx.MethodName);
|
||||||
|
Assert.False(loggerEx.Handled);
|
||||||
|
|
||||||
badListener.VerifyAll();
|
badListener.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,14 +66,25 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
||||||
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
||||||
.Throws(new Exception("listener"));
|
.Throws(new Exception("listener"));
|
||||||
var listener = new FunctionListener(badListener.Object, fd, trace);
|
var listener = new FunctionListener(badListener.Object, fd, trace, _loggerFactory);
|
||||||
|
|
||||||
await listener.StartAsync(ct);
|
await listener.StartAsync(ct);
|
||||||
|
|
||||||
Assert.Equal("The listener for function 'testfunc' was unable to start.", trace.Traces[0].Message);
|
string expectedMessage = "The listener for function 'testfunc' was unable to start.";
|
||||||
var exc = trace.Traces[0].Exception as FunctionException;
|
|
||||||
Assert.Equal("testfunc", exc.MethodName);
|
// Validate TraceWriter
|
||||||
Assert.True(exc.Handled);
|
Assert.Equal(expectedMessage, trace.Traces[0].Message);
|
||||||
|
var traceEx = trace.Traces[0].Exception as FunctionException;
|
||||||
|
Assert.Equal("testfunc", traceEx.MethodName);
|
||||||
|
Assert.True(traceEx.Handled);
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var logMessage = _loggerProvider.CreatedLoggers.Single().LogMessages.Single();
|
||||||
|
Assert.Equal(expectedMessage, logMessage.FormattedMessage);
|
||||||
|
var loggerEx = logMessage.Exception as FunctionException;
|
||||||
|
Assert.Equal("testfunc", loggerEx.MethodName);
|
||||||
|
Assert.True(loggerEx.Handled);
|
||||||
|
|
||||||
badListener.VerifyAll();
|
badListener.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +95,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
|
||||||
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
|
||||||
.Throws(new Exception("listener"));
|
.Throws(new Exception("listener"));
|
||||||
var listener = new FunctionListener(badListener.Object, fd, trace);
|
var listener = new FunctionListener(badListener.Object, fd, trace, _loggerFactory);
|
||||||
|
|
||||||
await listener.StartAsync(ct);
|
await listener.StartAsync(ct);
|
||||||
// these should do nothing, as function listener had an exception on start
|
// these should do nothing, as function listener had an exception on start
|
||||||
|
@ -87,12 +116,13 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
.Returns(Task.FromResult(false));
|
.Returns(Task.FromResult(false));
|
||||||
goodListener.Setup(bl => bl.StopAsync(It.IsAny<CancellationToken>()))
|
goodListener.Setup(bl => bl.StopAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult(false));
|
.Returns(Task.FromResult(false));
|
||||||
var listener = new FunctionListener(goodListener.Object, fd, trace);
|
var listener = new FunctionListener(goodListener.Object, fd, trace, _loggerFactory);
|
||||||
|
|
||||||
await listener.StartAsync(ct);
|
await listener.StartAsync(ct);
|
||||||
await listener.StopAsync(ct);
|
await listener.StopAsync(ct);
|
||||||
|
|
||||||
Assert.Empty(trace.Traces);
|
Assert.Empty(trace.Traces);
|
||||||
|
Assert.Empty(_loggerProvider.CreatedLoggers.Single().LogMessages);
|
||||||
|
|
||||||
goodListener.VerifyAll();
|
goodListener.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,17 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Azure.WebJobs.Host.Executors;
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
using Microsoft.Azure.WebJobs.Host.Indexers;
|
using Microsoft.Azure.WebJobs.Host.Indexers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Listeners;
|
using Microsoft.Azure.WebJobs.Host.Listeners;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
using Microsoft.Azure.WebJobs.Host.Protocols;
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
using Microsoft.Azure.WebJobs.Host.TestCommon;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -43,6 +46,10 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
SingletonManager singletonManager = new SingletonManager();
|
SingletonManager singletonManager = new SingletonManager();
|
||||||
TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose);
|
TestTraceWriter traceWriter = new TestTraceWriter(TraceLevel.Verbose);
|
||||||
|
|
||||||
|
ILoggerFactory loggerFactory = new LoggerFactory();
|
||||||
|
TestLoggerProvider loggerProvider = new TestLoggerProvider();
|
||||||
|
loggerFactory.AddProvider(loggerProvider);
|
||||||
|
|
||||||
// create a bunch of function definitions that are disabled
|
// create a bunch of function definitions that are disabled
|
||||||
List<FunctionDefinition> functions = new List<FunctionDefinition>();
|
List<FunctionDefinition> functions = new List<FunctionDefinition>();
|
||||||
FunctionDescriptor descriptor = new FunctionDescriptor
|
FunctionDescriptor descriptor = new FunctionDescriptor
|
||||||
|
@ -55,13 +62,22 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
|
||||||
|
|
||||||
// Create the composite listener - this will fail if any of the
|
// Create the composite listener - this will fail if any of the
|
||||||
// function definitions indicate that they are not disabled
|
// function definitions indicate that they are not disabled
|
||||||
HostListenerFactory factory = new HostListenerFactory(functions, singletonManager, DefaultJobActivator.Instance, null, traceWriter);
|
HostListenerFactory factory = new HostListenerFactory(functions, singletonManager, DefaultJobActivator.Instance, null, traceWriter, loggerFactory);
|
||||||
IListener listener = await factory.CreateAsync(CancellationToken.None);
|
IListener listener = await factory.CreateAsync(CancellationToken.None);
|
||||||
|
|
||||||
|
string expectedMessage = $"Function '{descriptor.ShortName}' is disabled";
|
||||||
|
|
||||||
|
// Validate TraceWriter
|
||||||
Assert.Equal(1, traceWriter.Traces.Count);
|
Assert.Equal(1, traceWriter.Traces.Count);
|
||||||
Assert.Equal(TraceLevel.Info, traceWriter.Traces[0].Level);
|
Assert.Equal(TraceLevel.Info, traceWriter.Traces[0].Level);
|
||||||
Assert.Equal(TraceSource.Host, traceWriter.Traces[0].Source);
|
Assert.Equal(TraceSource.Host, traceWriter.Traces[0].Source);
|
||||||
Assert.Equal(string.Format("Function '{0}' is disabled", descriptor.ShortName), traceWriter.Traces[0].Message);
|
Assert.Equal(expectedMessage, traceWriter.Traces[0].Message);
|
||||||
|
|
||||||
|
// Validate Logger
|
||||||
|
var logMessage = loggerProvider.CreatedLoggers.Single().LogMessages.Single();
|
||||||
|
Assert.Equal(LogLevel.Information, logMessage.Level);
|
||||||
|
Assert.Equal(LogCategories.Startup, logMessage.Category);
|
||||||
|
Assert.Equal(expectedMessage, logMessage.FormattedMessage);
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable("EnvironmentSettingTrue", null);
|
Environment.SetEnvironmentVariable("EnvironmentSettingTrue", null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,481 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Executors;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Protocols;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
|
||||||
|
{
|
||||||
|
public class ApplicationInsightsLoggerTests
|
||||||
|
{
|
||||||
|
private readonly Guid _invocationId = Guid.NewGuid();
|
||||||
|
private readonly DateTime _startTime = DateTime.UtcNow;
|
||||||
|
private readonly DateTime _endTime;
|
||||||
|
private readonly string _triggerReason = "new queue message";
|
||||||
|
private readonly string _functionFullName = "Functions.TestFunction";
|
||||||
|
private readonly string _functionShortName = "TestFunction";
|
||||||
|
private readonly IDictionary<string, string> _arguments;
|
||||||
|
private readonly TestTelemetryChannel _channel = new TestTelemetryChannel();
|
||||||
|
private readonly string defaultIp = "0.0.0.0";
|
||||||
|
private readonly TelemetryClient _client;
|
||||||
|
private readonly int durationMs = 450;
|
||||||
|
|
||||||
|
public ApplicationInsightsLoggerTests()
|
||||||
|
{
|
||||||
|
_endTime = _startTime.AddMilliseconds(durationMs);
|
||||||
|
_arguments = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["queueMessage"] = "my message",
|
||||||
|
["anotherParam"] = "some value"
|
||||||
|
};
|
||||||
|
|
||||||
|
TelemetryConfiguration config = new TelemetryConfiguration
|
||||||
|
{
|
||||||
|
TelemetryChannel = _channel,
|
||||||
|
InstrumentationKey = "some key"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the same initializers that we use in the product code
|
||||||
|
DefaultTelemetryClientFactory.AddInitializers(config);
|
||||||
|
|
||||||
|
_client = new TelemetryClient(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LogFunctionResult_Succeeded_SendsCorrectTelemetry()
|
||||||
|
{
|
||||||
|
var result = CreateDefaultInstanceLogEntry();
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Results);
|
||||||
|
|
||||||
|
using (logger.BeginFunctionScope(CreateFunctionInstance(_invocationId)))
|
||||||
|
{
|
||||||
|
logger.LogFunctionResult(_functionShortName, result, TimeSpan.FromMilliseconds(durationMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestTelemetry telemetry = _channel.Telemetries.Single() as RequestTelemetry;
|
||||||
|
|
||||||
|
Assert.Equal(_invocationId.ToString(), telemetry.Id);
|
||||||
|
Assert.Equal(_invocationId.ToString(), telemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Name);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Context.Operation.Name);
|
||||||
|
Assert.Equal(defaultIp, telemetry.Context.Location.Ip);
|
||||||
|
// TODO: Beef up validation to include properties
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LogFunctionResult_Failed_SendsCorrectTelemetry()
|
||||||
|
{
|
||||||
|
var result = CreateDefaultInstanceLogEntry();
|
||||||
|
FunctionInvocationException fex = new FunctionInvocationException("Failed");
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Results);
|
||||||
|
|
||||||
|
using (logger.BeginFunctionScope(CreateFunctionInstance(_invocationId)))
|
||||||
|
{
|
||||||
|
logger.LogFunctionResult(_functionShortName, result, TimeSpan.FromMilliseconds(durationMs), fex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors log an associated Exception
|
||||||
|
RequestTelemetry requestTelemetry = _channel.Telemetries.OfType<RequestTelemetry>().Single();
|
||||||
|
ExceptionTelemetry exceptionTelemetry = _channel.Telemetries.OfType<ExceptionTelemetry>().Single();
|
||||||
|
|
||||||
|
Assert.Equal(2, _channel.Telemetries.Count);
|
||||||
|
Assert.Equal(_invocationId.ToString(), requestTelemetry.Id);
|
||||||
|
Assert.Equal(_invocationId.ToString(), requestTelemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, requestTelemetry.Name);
|
||||||
|
Assert.Equal(_functionShortName, requestTelemetry.Context.Operation.Name);
|
||||||
|
Assert.Equal(defaultIp, requestTelemetry.Context.Location.Ip);
|
||||||
|
// TODO: Beef up validation to include properties
|
||||||
|
|
||||||
|
// Exception needs to have associated id
|
||||||
|
Assert.Equal(_invocationId.ToString(), exceptionTelemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, exceptionTelemetry.Context.Operation.Name);
|
||||||
|
Assert.Same(fex, exceptionTelemetry.Exception);
|
||||||
|
// TODO: Beef up validation to include properties
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LogFunctionAggregate_SendsCorrectTelemetry()
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
var resultAggregate = new FunctionResultAggregate
|
||||||
|
{
|
||||||
|
Name = _functionFullName,
|
||||||
|
Failures = 4,
|
||||||
|
Successes = 116,
|
||||||
|
MinDuration = TimeSpan.FromMilliseconds(200),
|
||||||
|
MaxDuration = TimeSpan.FromMilliseconds(2180),
|
||||||
|
AverageDuration = TimeSpan.FromMilliseconds(340),
|
||||||
|
Timestamp = now
|
||||||
|
};
|
||||||
|
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Aggregator);
|
||||||
|
logger.LogFunctionResultAggregate(resultAggregate);
|
||||||
|
|
||||||
|
IEnumerable<MetricTelemetry> metrics = _channel.Telemetries.Cast<MetricTelemetry>();
|
||||||
|
// turn them into a dictionary so we can easily validate
|
||||||
|
IDictionary<string, double> metricDict = metrics.ToDictionary(m => m.Name, m => m.Value);
|
||||||
|
|
||||||
|
Assert.Equal(7, metricDict.Count);
|
||||||
|
Assert.Equal(4, metricDict[$"{_functionFullName} {LoggingKeys.Failures}"]);
|
||||||
|
Assert.Equal(116, metricDict[$"{_functionFullName} {LoggingKeys.Successes}"]);
|
||||||
|
Assert.Equal(200, metricDict[$"{_functionFullName} {LoggingKeys.MinDuration}"]);
|
||||||
|
Assert.Equal(2180, metricDict[$"{_functionFullName} {LoggingKeys.MaxDuration}"]);
|
||||||
|
Assert.Equal(340, metricDict[$"{_functionFullName} {LoggingKeys.AvgDuration}"]);
|
||||||
|
Assert.Equal(96.67, metricDict[$"{_functionFullName} {LoggingKeys.SuccessRate}"]);
|
||||||
|
Assert.Equal(120, metricDict[$"{_functionFullName} {LoggingKeys.Count}"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LogFunctionResult_HttpRequest_SendsCorrectTelemetry()
|
||||||
|
{
|
||||||
|
// If the scope has an HttpRequestMessage, we'll use the proper values
|
||||||
|
// for the RequestTelemetry
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
var result = CreateDefaultInstanceLogEntry();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "http://someuri/api/path");
|
||||||
|
request.Headers.Add("User-Agent", "my custom user agent");
|
||||||
|
var response = new HttpResponseMessage();
|
||||||
|
request.Properties[ScopeKeys.FunctionsHttpResponse] = response;
|
||||||
|
|
||||||
|
MockIpAddress(request, "1.2.3.4");
|
||||||
|
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Results);
|
||||||
|
var scopeProps = CreateScopeDictionary(_invocationId, _functionShortName);
|
||||||
|
scopeProps[ScopeKeys.HttpRequest] = request;
|
||||||
|
|
||||||
|
using (logger.BeginScope(scopeProps))
|
||||||
|
{
|
||||||
|
logger.LogFunctionResult(_functionShortName, result, TimeSpan.FromMilliseconds(durationMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestTelemetry telemetry = _channel.Telemetries.Single() as RequestTelemetry;
|
||||||
|
|
||||||
|
Assert.Equal(_invocationId.ToString(), telemetry.Id);
|
||||||
|
Assert.Equal(_invocationId.ToString(), telemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Name);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Context.Operation.Name);
|
||||||
|
Assert.Equal("1.2.3.4", telemetry.Context.Location.Ip);
|
||||||
|
Assert.Equal("POST", telemetry.Properties[LoggingKeys.HttpMethod]);
|
||||||
|
Assert.Equal(new Uri("http://someuri/api/path"), telemetry.Url);
|
||||||
|
Assert.Equal("my custom user agent", telemetry.Context.User.UserAgent);
|
||||||
|
Assert.Equal("200", telemetry.ResponseCode);
|
||||||
|
// TODO: Beef up validation to include properties
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LogFunctionResult_HttpRequest_WithException_SendsCorrectTelemetry()
|
||||||
|
{
|
||||||
|
// If the scope has an HttpRequestMessage, we'll use the proper values
|
||||||
|
// for the RequestTelemetry
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
var result = CreateDefaultInstanceLogEntry();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, "http://someuri/api/path");
|
||||||
|
request.Headers.Add("User-Agent", "my custom user agent");
|
||||||
|
|
||||||
|
// In the case of an exception being thrown, no response is attached
|
||||||
|
|
||||||
|
MockIpAddress(request, "1.2.3.4");
|
||||||
|
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Results);
|
||||||
|
var scopeProps = CreateScopeDictionary(_invocationId, _functionShortName);
|
||||||
|
scopeProps[ScopeKeys.HttpRequest] = request;
|
||||||
|
|
||||||
|
using (logger.BeginScope(scopeProps))
|
||||||
|
{
|
||||||
|
logger.LogFunctionResult(_functionShortName, result, TimeSpan.FromMilliseconds(durationMs), new Exception("Boom"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// one Exception, one Request
|
||||||
|
Assert.Equal(2, _channel.Telemetries.Count);
|
||||||
|
|
||||||
|
RequestTelemetry requestTelemetry = _channel.Telemetries.Where(t => t is RequestTelemetry).Single() as RequestTelemetry;
|
||||||
|
ExceptionTelemetry exceptionTelemetry = _channel.Telemetries.Where(t => t is ExceptionTelemetry).Single() as ExceptionTelemetry;
|
||||||
|
|
||||||
|
Assert.Equal(_invocationId.ToString(), requestTelemetry.Id);
|
||||||
|
Assert.Equal(_invocationId.ToString(), requestTelemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, requestTelemetry.Name);
|
||||||
|
Assert.Equal(_functionShortName, requestTelemetry.Context.Operation.Name);
|
||||||
|
Assert.Equal("1.2.3.4", requestTelemetry.Context.Location.Ip);
|
||||||
|
Assert.Equal("POST", requestTelemetry.Properties[LoggingKeys.HttpMethod]);
|
||||||
|
Assert.Equal(new Uri("http://someuri/api/path"), requestTelemetry.Url);
|
||||||
|
Assert.Equal("my custom user agent", requestTelemetry.Context.User.UserAgent);
|
||||||
|
Assert.Equal("500", requestTelemetry.ResponseCode);
|
||||||
|
// TODO: Beef up validation to include properties
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Log_NoProperties_CreatesTraceAndCorrelates()
|
||||||
|
{
|
||||||
|
Guid scopeGuid = Guid.NewGuid();
|
||||||
|
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Function);
|
||||||
|
using (logger.BeginFunctionScope(CreateFunctionInstance(scopeGuid)))
|
||||||
|
{
|
||||||
|
logger.LogInformation("Information");
|
||||||
|
logger.LogCritical("Critical");
|
||||||
|
logger.LogDebug("Debug");
|
||||||
|
logger.LogError("Error");
|
||||||
|
logger.LogTrace("Trace");
|
||||||
|
logger.LogWarning("Warning");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(6, _channel.Telemetries.Count);
|
||||||
|
Assert.Equal(6, _channel.Telemetries.OfType<TraceTelemetry>().Count());
|
||||||
|
foreach (var telemetry in _channel.Telemetries.Cast<TraceTelemetry>())
|
||||||
|
{
|
||||||
|
SeverityLevel expectedLevel;
|
||||||
|
if (telemetry.Message == "Trace" || telemetry.Message == "Debug")
|
||||||
|
{
|
||||||
|
expectedLevel = SeverityLevel.Verbose;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.True(Enum.TryParse(telemetry.Message, out expectedLevel));
|
||||||
|
}
|
||||||
|
Assert.Equal(expectedLevel, telemetry.SeverityLevel);
|
||||||
|
|
||||||
|
Assert.Equal(LogCategories.Function, telemetry.Properties[LoggingKeys.CategoryName]);
|
||||||
|
Assert.Equal(telemetry.Message, telemetry.Properties[LoggingKeys.CustomPropertyPrefix + LoggingKeys.OriginalFormat]);
|
||||||
|
Assert.Equal(scopeGuid.ToString(), telemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Context.Operation.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Log_WithProperties_IncludesProps()
|
||||||
|
{
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Function);
|
||||||
|
logger.LogInformation("Using {some} custom {properties}. {Test}.", "1", 2, "3");
|
||||||
|
|
||||||
|
var telemetry = _channel.Telemetries.Single() as TraceTelemetry;
|
||||||
|
|
||||||
|
Assert.Equal(SeverityLevel.Information, telemetry.SeverityLevel);
|
||||||
|
|
||||||
|
Assert.Equal(LogCategories.Function, telemetry.Properties[LoggingKeys.CategoryName]);
|
||||||
|
Assert.Equal("Using {some} custom {properties}. {Test}.",
|
||||||
|
telemetry.Properties[LoggingKeys.CustomPropertyPrefix + LoggingKeys.OriginalFormat]);
|
||||||
|
Assert.Equal("Using 1 custom 2. 3.", telemetry.Message);
|
||||||
|
Assert.Equal("1", telemetry.Properties[LoggingKeys.CustomPropertyPrefix + "some"]);
|
||||||
|
Assert.Equal("2", telemetry.Properties[LoggingKeys.CustomPropertyPrefix + "properties"]);
|
||||||
|
Assert.Equal("3", telemetry.Properties[LoggingKeys.CustomPropertyPrefix + "Test"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Log_WithException_CreatesExceptionAndCorrelates()
|
||||||
|
{
|
||||||
|
var ex = new InvalidOperationException("Failure");
|
||||||
|
Guid scopeGuid = Guid.NewGuid();
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Function);
|
||||||
|
|
||||||
|
using (logger.BeginFunctionScope(CreateFunctionInstance(scopeGuid)))
|
||||||
|
{
|
||||||
|
logger.LogError(0, ex, "Error with customer: {customer}.", "John Doe");
|
||||||
|
}
|
||||||
|
|
||||||
|
var telemetry = _channel.Telemetries.Single() as ExceptionTelemetry;
|
||||||
|
|
||||||
|
Assert.Equal(SeverityLevel.Error, telemetry.SeverityLevel);
|
||||||
|
|
||||||
|
Assert.Equal(LogCategories.Function, telemetry.Properties[LoggingKeys.CategoryName]);
|
||||||
|
Assert.Equal("Error with customer: {customer}.",
|
||||||
|
telemetry.Properties[LoggingKeys.CustomPropertyPrefix + LoggingKeys.OriginalFormat]);
|
||||||
|
Assert.Equal("Error with customer: John Doe.", telemetry.Message);
|
||||||
|
Assert.Equal("John Doe", telemetry.Properties[LoggingKeys.CustomPropertyPrefix + "customer"]);
|
||||||
|
Assert.Same(ex, telemetry.Exception);
|
||||||
|
Assert.Equal(scopeGuid.ToString(), telemetry.Context.Operation.Id);
|
||||||
|
Assert.Equal(_functionShortName, telemetry.Context.Operation.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("1.2.3.4:5")]
|
||||||
|
[InlineData("1.2.3.4")]
|
||||||
|
public void GetIpAddress_ChecksHeaderFirst(string headerIp)
|
||||||
|
{
|
||||||
|
HttpRequestMessage request = new HttpRequestMessage();
|
||||||
|
request.Headers.Add(ScopeKeys.ForwardedForHeaderName, headerIp);
|
||||||
|
MockIpAddress(request, "5.6.7.8");
|
||||||
|
|
||||||
|
string ip = ApplicationInsightsLogger.GetIpAddress(request);
|
||||||
|
|
||||||
|
Assert.Equal("1.2.3.4", ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetIpAddress_ChecksContextSecond()
|
||||||
|
{
|
||||||
|
HttpRequestMessage request = new HttpRequestMessage();
|
||||||
|
MockIpAddress(request, "5.6.7.8");
|
||||||
|
|
||||||
|
string ip = ApplicationInsightsLogger.GetIpAddress(request);
|
||||||
|
|
||||||
|
Assert.Equal("5.6.7.8", ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BeginScope()
|
||||||
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
tasks.Add(Level1(Guid.NewGuid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Level1(Guid asyncLocalSetting)
|
||||||
|
{
|
||||||
|
// Push and pop values onto the dictionary at various levels. Make sure they
|
||||||
|
// maintain their AsyncLocal state
|
||||||
|
var level1 = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["AsyncLocal"] = asyncLocalSetting,
|
||||||
|
["1"] = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ILogger logger = CreateLogger(LogCategories.Function);
|
||||||
|
using (logger.BeginScope(level1))
|
||||||
|
{
|
||||||
|
ValidateScope(level1);
|
||||||
|
|
||||||
|
await Level2(asyncLocalSetting);
|
||||||
|
|
||||||
|
ValidateScope(level1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Level2(Guid asyncLocalSetting)
|
||||||
|
{
|
||||||
|
await Task.Delay(1);
|
||||||
|
|
||||||
|
var level2 = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["2"] = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedLevel2 = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["1"] = 1,
|
||||||
|
["2"] = 2,
|
||||||
|
["AsyncLocal"] = asyncLocalSetting
|
||||||
|
};
|
||||||
|
|
||||||
|
ILogger logger2 = CreateLogger(LogCategories.Function);
|
||||||
|
using (logger2.BeginScope(level2))
|
||||||
|
{
|
||||||
|
ValidateScope(expectedLevel2);
|
||||||
|
|
||||||
|
await Level3(asyncLocalSetting);
|
||||||
|
|
||||||
|
ValidateScope(expectedLevel2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Level3(Guid asyncLocalSetting)
|
||||||
|
{
|
||||||
|
await Task.Delay(1);
|
||||||
|
|
||||||
|
// also overwrite value 1, we expect this to win here
|
||||||
|
var level3 = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["1"] = 11,
|
||||||
|
["3"] = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedLevel3 = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["1"] = 11,
|
||||||
|
["2"] = 2,
|
||||||
|
["3"] = 3,
|
||||||
|
["AsyncLocal"] = asyncLocalSetting
|
||||||
|
};
|
||||||
|
|
||||||
|
ILogger logger3 = CreateLogger(LogCategories.Function);
|
||||||
|
using (logger3.BeginScope(level3))
|
||||||
|
{
|
||||||
|
ValidateScope(expectedLevel3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MockIpAddress(HttpRequestMessage request, string ipAddress)
|
||||||
|
{
|
||||||
|
Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>(MockBehavior.Strict);
|
||||||
|
Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>(MockBehavior.Strict);
|
||||||
|
mockRequest.Setup(r => r.UserHostAddress).Returns(ipAddress);
|
||||||
|
mockContext.Setup(c => c.Request).Returns(mockRequest.Object);
|
||||||
|
request.Properties[ScopeKeys.HttpContext] = mockContext.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILogger CreateLogger(string category)
|
||||||
|
{
|
||||||
|
return new ApplicationInsightsLogger(_client, category, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateScope(IDictionary<string, object> expected)
|
||||||
|
{
|
||||||
|
var scopeDict = DictionaryLoggerScope.GetMergedStateDictionary();
|
||||||
|
Assert.Equal(expected.Count, scopeDict.Count);
|
||||||
|
foreach (var entry in expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(entry.Value, scopeDict[entry.Key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IFunctionInstance CreateFunctionInstance(Guid id)
|
||||||
|
{
|
||||||
|
var descriptor = new FunctionDescriptor
|
||||||
|
{
|
||||||
|
Method = GetType().GetMethod(nameof(TestFunction), BindingFlags.NonPublic | BindingFlags.Static)
|
||||||
|
};
|
||||||
|
|
||||||
|
return new FunctionInstance(id, null, new ExecutionReason(), null, null, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IDictionary<string, object> CreateScopeDictionary(Guid invocationId, string functionName)
|
||||||
|
{
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
[ScopeKeys.FunctionInvocationId] = invocationId,
|
||||||
|
[ScopeKeys.FunctionName] = functionName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TestFunction()
|
||||||
|
{
|
||||||
|
// used for a FunctionDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionInstanceLogEntry CreateDefaultInstanceLogEntry()
|
||||||
|
{
|
||||||
|
return new FunctionInstanceLogEntry
|
||||||
|
{
|
||||||
|
FunctionName = _functionFullName,
|
||||||
|
FunctionInstanceId = _invocationId,
|
||||||
|
StartTime = _startTime,
|
||||||
|
EndTime = _endTime,
|
||||||
|
LogOutput = "a bunch of output that we will not forward", // not used here -- this is all Traced
|
||||||
|
TriggerReason = _triggerReason,
|
||||||
|
ParentId = Guid.NewGuid(), // we do not track this
|
||||||
|
ErrorDetails = null, // we do not use this -- we pass the exception in separately
|
||||||
|
Arguments = _arguments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
|
||||||
|
{
|
||||||
|
public class FormattedLogValuesCollectionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Verify_ToString_AndCollection()
|
||||||
|
{
|
||||||
|
var additionalPayload = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123
|
||||||
|
};
|
||||||
|
|
||||||
|
var payload = new FormattedLogValuesCollection("This {string} has some {data}",
|
||||||
|
new object[] { "xyz", 789 }, new ReadOnlyDictionary<string, object>(additionalPayload));
|
||||||
|
|
||||||
|
// Verify string behavior
|
||||||
|
Assert.Equal("This xyz has some 789", payload.ToString());
|
||||||
|
|
||||||
|
// Verify the collection
|
||||||
|
var expectedPayload = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123,
|
||||||
|
["string"] = "xyz",
|
||||||
|
["data"] = 789,
|
||||||
|
|
||||||
|
// FormattedLogValues adds this automatically to capture the string template
|
||||||
|
["{OriginalFormat}"] = "This {string} has some {data}"
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidateCollection(expectedPayload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoAdditionalPayload()
|
||||||
|
{
|
||||||
|
var payload = new FormattedLogValuesCollection("This {string} has some {data}",
|
||||||
|
new object[] { "xyz", 789 }, null);
|
||||||
|
|
||||||
|
// Verify string behavior
|
||||||
|
Assert.Equal("This xyz has some 789", payload.ToString());
|
||||||
|
|
||||||
|
// Verify the collection
|
||||||
|
var expectedPayload = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["string"] = "xyz",
|
||||||
|
["data"] = 789,
|
||||||
|
|
||||||
|
// FormattedLogValues adds this automatically to capture the string template
|
||||||
|
["{OriginalFormat}"] = "This {string} has some {data}"
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidateCollection(expectedPayload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NoStringPayload()
|
||||||
|
{
|
||||||
|
var additionalPayload = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123
|
||||||
|
};
|
||||||
|
|
||||||
|
var payload = new FormattedLogValuesCollection("This string has no data",
|
||||||
|
null, new ReadOnlyDictionary<string, object>(additionalPayload));
|
||||||
|
|
||||||
|
// Verify string behavior
|
||||||
|
Assert.Equal("This string has no data", payload.ToString());
|
||||||
|
|
||||||
|
// Verify the collection
|
||||||
|
var expectedPayload = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123,
|
||||||
|
|
||||||
|
// FormattedLogValues adds this automatically to capture the string template
|
||||||
|
["{OriginalFormat}"] = "This string has no data"
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidateCollection(expectedPayload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[InlineData("")]
|
||||||
|
public void NoString(string message)
|
||||||
|
{
|
||||||
|
var additionalPayload = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123
|
||||||
|
};
|
||||||
|
|
||||||
|
var payload = new FormattedLogValuesCollection(message,
|
||||||
|
null, new ReadOnlyDictionary<string, object>(additionalPayload));
|
||||||
|
|
||||||
|
// Verify string behavior
|
||||||
|
// FormattedLogValues changes nulls to [null]
|
||||||
|
var expectedMessage = message ?? "[null]";
|
||||||
|
Assert.Equal(expectedMessage, payload.ToString());
|
||||||
|
|
||||||
|
// Verify the collection
|
||||||
|
var expectedPayload = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123,
|
||||||
|
|
||||||
|
// FormattedLogValues adds this automatically to capture the string template
|
||||||
|
["{OriginalFormat}"] = expectedMessage
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidateCollection(expectedPayload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NullValues_WithCurlyBraces()
|
||||||
|
{
|
||||||
|
var additionalPayload = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123
|
||||||
|
};
|
||||||
|
|
||||||
|
var message = "{this} {should} {work} {";
|
||||||
|
var payload = new FormattedLogValuesCollection(message,
|
||||||
|
null, new ReadOnlyDictionary<string, object>(additionalPayload));
|
||||||
|
|
||||||
|
// Verify string behavior
|
||||||
|
Assert.Equal(message, payload.ToString());
|
||||||
|
|
||||||
|
// Verify the collection
|
||||||
|
var expectedPayload = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["additional"] = "abc",
|
||||||
|
["payload"] = 123,
|
||||||
|
|
||||||
|
// FormattedLogValues adds this automatically to capture the string template
|
||||||
|
["{OriginalFormat}"] = message
|
||||||
|
};
|
||||||
|
|
||||||
|
ValidateCollection(expectedPayload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateCollection(IDictionary<string, object> expectedPayload,
|
||||||
|
IReadOnlyList<KeyValuePair<string, object>> actualPayload)
|
||||||
|
{
|
||||||
|
Assert.Equal(expectedPayload.Count, actualPayload.Count);
|
||||||
|
foreach (var kvp in actualPayload)
|
||||||
|
{
|
||||||
|
Assert.Equal(expectedPayload[kvp.Key], kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
|
||||||
|
{
|
||||||
|
public class FunctionResultAggregatorConfigurationTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DefaultValues()
|
||||||
|
{
|
||||||
|
var config = new FunctionResultAggregatorConfiguration();
|
||||||
|
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(30), config.FlushTimeout);
|
||||||
|
Assert.Equal(1000, config.BatchSize);
|
||||||
|
Assert.Equal(true, config.IsEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, true)]
|
||||||
|
[InlineData(10001, true)]
|
||||||
|
[InlineData(1, false)]
|
||||||
|
[InlineData(10000, false)]
|
||||||
|
public void BatchSize_Limits(int batchSize, bool throws)
|
||||||
|
{
|
||||||
|
var config = new FunctionResultAggregatorConfiguration();
|
||||||
|
ArgumentOutOfRangeException caughtEx = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config.BatchSize = batchSize;
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException ex)
|
||||||
|
{
|
||||||
|
caughtEx = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(throws, caughtEx != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object[][] TimeSpanData = new object[][] {
|
||||||
|
new object[] { TimeSpan.FromSeconds(300), false },
|
||||||
|
new object[] { TimeSpan.FromSeconds(0), true },
|
||||||
|
new object[] { TimeSpan.FromSeconds(301), true },
|
||||||
|
new object[] { TimeSpan.FromSeconds(1), false },
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(TimeSpanData))]
|
||||||
|
public void FlushTimeout_Limits(TimeSpan flushTimeout, bool throws)
|
||||||
|
{
|
||||||
|
var config = new FunctionResultAggregatorConfiguration();
|
||||||
|
ArgumentOutOfRangeException caughtEx = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config.FlushTimeout = flushTimeout;
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException ex)
|
||||||
|
{
|
||||||
|
caughtEx = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(throws, caughtEx != null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.WebJobs.Host.Loggers;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
|
||||||
|
{
|
||||||
|
public class FunctionResultAggregatorTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1)]
|
||||||
|
[InlineData(1000)]
|
||||||
|
public async Task Aggregator_Flushes_WhenBatchIsFull(int batchSize)
|
||||||
|
{
|
||||||
|
int publishCalls = 0;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
Assert.Equal(batchSize, props[LoggingKeys.Successes]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromMinutes(1), factory);
|
||||||
|
|
||||||
|
await AddSuccessResults(aggregator, batchSize * 5);
|
||||||
|
|
||||||
|
// allow a flush
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
|
Assert.Equal((batchSize * 5) / batchSize, publishCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Aggregator_Flushes_WhenTimerFires()
|
||||||
|
{
|
||||||
|
int batchSize = 1000;
|
||||||
|
int numberToInsert = 10;
|
||||||
|
int publishCalls = 0;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
Assert.Equal(numberToInsert, props[LoggingKeys.Successes]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromSeconds(1), factory);
|
||||||
|
|
||||||
|
await AddSuccessResults(aggregator, numberToInsert);
|
||||||
|
|
||||||
|
// allow the timer to fire
|
||||||
|
await Task.Delay(1100);
|
||||||
|
|
||||||
|
Assert.Equal(1, publishCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Aggregator_AlternatesTimerAndBatch()
|
||||||
|
{
|
||||||
|
int publishCalls = 0;
|
||||||
|
int totalSuccesses = 0;
|
||||||
|
int batchSize = 100;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
totalSuccesses += Convert.ToInt32(props[LoggingKeys.Successes]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromSeconds(1), factory);
|
||||||
|
|
||||||
|
// do this loop twice to ensure it continues working
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
// insert 225. Expect 2 calls to publish, then a flush for 25.
|
||||||
|
await AddSuccessResults(aggregator, 225);
|
||||||
|
|
||||||
|
await Task.Delay(1100);
|
||||||
|
Assert.Equal(3 * (i + 1), publishCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(6, publishCalls);
|
||||||
|
Assert.Equal(450, totalSuccesses);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Aggregator_FlushesOnCancelation()
|
||||||
|
{
|
||||||
|
int batchSize = 100;
|
||||||
|
int publishCalls = 0;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
Assert.Equal(10, props[LoggingKeys.Successes]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromSeconds(1), factory);
|
||||||
|
|
||||||
|
await AddSuccessResults(aggregator, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Aggregator_Trims_DefaultClassName()
|
||||||
|
{
|
||||||
|
int batchSize = 10;
|
||||||
|
int publishCalls = 0;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
Assert.Equal(10, props[LoggingKeys.Successes]);
|
||||||
|
Assert.Equal("SomeTest", props[LoggingKeys.Name]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromSeconds(1), factory);
|
||||||
|
|
||||||
|
await AddSuccessResults(aggregator, 10, "Functions.SomeTest");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Aggregator_DoesNotTrim_NonDefaultClassName()
|
||||||
|
{
|
||||||
|
int batchSize = 10;
|
||||||
|
int publishCalls = 0;
|
||||||
|
|
||||||
|
ILoggerFactory factory = CreateMockLoggerFactory((props) =>
|
||||||
|
{
|
||||||
|
publishCalls++;
|
||||||
|
Assert.Equal(10, props[LoggingKeys.Successes]);
|
||||||
|
Assert.Equal("AnotherClass.SomeTest", props[LoggingKeys.Name]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var aggregator = new FunctionResultAggregator(batchSize, TimeSpan.FromSeconds(1), factory);
|
||||||
|
|
||||||
|
await AddSuccessResults(aggregator, 10, "AnotherClass.SomeTest");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task AddSuccessResults(IAsyncCollector<FunctionInstanceLogEntry> aggregator, int count, string functionName = null)
|
||||||
|
{
|
||||||
|
// Simulate the real executor, where a "function start" and "function end" are both added.
|
||||||
|
// The aggregator will filter out any with EndTime == null
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
await aggregator.AddAsync(new FunctionInstanceLogEntry
|
||||||
|
{
|
||||||
|
FunctionName = functionName,
|
||||||
|
ErrorDetails = null,
|
||||||
|
EndTime = null
|
||||||
|
});
|
||||||
|
|
||||||
|
await aggregator.AddAsync(new FunctionInstanceLogEntry
|
||||||
|
{
|
||||||
|
FunctionName = functionName,
|
||||||
|
ErrorDetails = null,
|
||||||
|
EndTime = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ILoggerFactory CreateMockLoggerFactory(Action<IDictionary<string, object>> logAction)
|
||||||
|
{
|
||||||
|
ILogger logger = new MockLogger(logAction);
|
||||||
|
|
||||||
|
Mock<ILoggerFactory> mockFactory = new Mock<ILoggerFactory>();
|
||||||
|
mockFactory
|
||||||
|
.Setup(lf => lf.CreateLogger(It.IsAny<string>()))
|
||||||
|
.Returns(logger);
|
||||||
|
|
||||||
|
return mockFactory.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockLogger : ILogger
|
||||||
|
{
|
||||||
|
private Action<IDictionary<string, object>> _logAction;
|
||||||
|
|
||||||
|
public MockLogger(Action<IDictionary<string, object>> logAction)
|
||||||
|
{
|
||||||
|
_logAction = logAction;
|
||||||
|
}
|
||||||
|
public IDisposable BeginScope<TState>(TState state)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel)
|
||||||
|
{
|
||||||
|
// in these tests, we will only ever see Information;
|
||||||
|
Assert.Equal(LogLevel.Information, logLevel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
|
{
|
||||||
|
IEnumerable<KeyValuePair<string, object>> props = state as IEnumerable<KeyValuePair<string, object>>;
|
||||||
|
Assert.NotNull(props);
|
||||||
|
|
||||||
|
_logAction(props.ToDictionary(k => k.Key, k => k.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче