adding initial ILogger/AppInsights support

This commit is contained in:
Brett Samblanet 2017-03-11 10:35:33 -08:00
Родитель c533e4cf67
Коммит 61aa424616
114 изменённых файлов: 4961 добавлений и 432 удалений

Просмотреть файл

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
</assemblyBinding>
</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>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

Просмотреть файл

@ -2,7 +2,10 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Extensions.Logging;
namespace SampleHost
{
@ -19,6 +22,24 @@ namespace SampleHost
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);
var host = new JobHost(config);

Просмотреть файл

@ -34,18 +34,116 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<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">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<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.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.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<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>
<Compile Include="Functions.cs" />

Просмотреть файл

@ -1,4 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<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="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>

Просмотреть файл

@ -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.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 TraceWriterBinding : IBinding
{
private readonly ParameterInfo _parameter;
private ILoggerFactory _loggerFactory;
public TraceWriterBinding(ParameterInfo parameter)
public TraceWriterBinding(ParameterInfo parameter, ILoggerFactory loggerFactory)
{
_parameter = parameter;
_loggerFactory = loggerFactory;
}
public bool FromAttribute
@ -45,16 +49,24 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
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;
if (_parameter.ParameterType == typeof(TraceWriter))
{
// bind directly to the context TraceWriter
tracer = context.Trace;
tracer = trace;
}
else
{
// bind to an adapter
tracer = TextWriterTraceAdapter.Synchronized(context.Trace);
tracer = TextWriterTraceAdapter.Synchronized(trace);
}
return BindAsync(tracer, context.ValueContext);

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Bindings
{
@ -15,6 +16,13 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
/// </summary>
internal class TraceWriterBindingProvider : IBindingProvider
{
private ILoggerFactory _loggerFactory;
public TraceWriterBindingProvider(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public Task<IBinding> TryCreateAsync(BindingProviderContext context)
{
if (context == null)
@ -29,7 +37,7 @@ namespace Microsoft.Azure.WebJobs.Host.Bindings
return Task.FromResult<IBinding>(null);
}
IBinding binding = new TraceWriterBinding(parameter);
IBinding binding = new TraceWriterBinding(parameter, _loggerFactory);
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.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
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 ISharedContextProvider _sharedContextProvider;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private readonly string _functionId;
private readonly IStorageAccount _hostAccount;
private readonly IStorageAccount _dataAccount;
@ -42,6 +44,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
TraceWriter trace,
ILoggerFactory loggerFactory,
string functionId,
IStorageAccount hostAccount,
IStorageAccount dataAccount,
@ -128,6 +131,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
_sharedContextProvider = sharedContextProvider;
_trace = trace;
_loggerFactory = loggerFactory;
_functionId = functionId;
_hostAccount = hostAccount;
_dataAccount = dataAccount;
@ -168,7 +172,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
// notification queue and dispatch to the target job function.
SharedBlobQueueListener sharedBlobQueueListener = _sharedContextProvider.GetOrCreateInstance<SharedBlobQueueListener>(
new SharedBlobQueueListenerFactory(_hostAccount, sharedQueueWatcher, hostBlobTriggerQueue,
_queueConfiguration, _exceptionHandler, _trace, sharedBlobListener.BlobWritterWatcher));
_queueConfiguration, _exceptionHandler, _trace, _loggerFactory, sharedBlobListener.BlobWritterWatcher));
var queueListener = new BlobListener(sharedBlobQueueListener);
// 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.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
using Newtonsoft.Json;
@ -24,6 +25,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
private readonly TraceWriter _trace;
private readonly IBlobWrittenWatcher _blobWrittenWatcher;
private readonly IStorageAccount _hostAccount;
private readonly ILoggerFactory _loggerFactory;
public SharedBlobQueueListenerFactory(
IStorageAccount hostAccount,
@ -32,6 +34,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
IQueueConfiguration queueConfiguration,
IWebJobsExceptionHandler exceptionHandler,
TraceWriter trace,
ILoggerFactory loggerFactory,
IBlobWrittenWatcher blobWrittenWatcher)
{
if (hostAccount == null)
@ -75,6 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Listeners
_queueConfiguration = queueConfiguration;
_exceptionHandler = exceptionHandler;
_trace = trace;
_loggerFactory = loggerFactory;
_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
// 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);
IListener listener = new QueueListener(_hostBlobTriggerQueue, defaultPoisonQueue, triggerExecutor,
_exceptionHandler, _trace, _sharedQueueWatcher, _queueConfiguration, queueProcessor);
_exceptionHandler, _trace, _loggerFactory, _sharedQueueWatcher, _queueConfiguration, queueProcessor);
return new SharedBlobQueueListener(listener, triggerExecutor);
}

Просмотреть файл

@ -8,7 +8,6 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Converters;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Indexers;
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.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
@ -35,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
private readonly ISharedContextProvider _sharedContextProvider;
private readonly SingletonManager _singletonManager;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
public BlobTriggerAttributeBindingProvider(INameResolver nameResolver,
IStorageAccountProvider accountProvider,
@ -47,7 +48,8 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
SingletonManager singletonManager,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
if (accountProvider == null)
{
@ -116,6 +118,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
_sharedContextProvider = sharedContextProvider;
_singletonManager = singletonManager;
_trace = trace;
_loggerFactory = loggerFactory;
}
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,
_hostIdProvider, _queueConfiguration, _blobsConfiguration, _exceptionHandler, _blobWrittenWatcherSetter,
_messageEnqueuedWatcherSetter, _sharedContextProvider, _singletonManager, _trace);
_messageEnqueuedWatcherSetter, _sharedContextProvider, _singletonManager, _trace, _loggerFactory);
return binding;
}

Просмотреть файл

@ -17,6 +17,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
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 ISharedContextProvider _sharedContextProvider;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private readonly IAsyncObjectToTypeConverter<IStorageBlob> _converter;
private readonly IReadOnlyDictionary<string, Type> _bindingDataContract;
private readonly SingletonManager _singletonManager;
@ -55,7 +57,8 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
SingletonManager singletonManager,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
if (parameter == null)
{
@ -147,6 +150,7 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
_sharedContextProvider = sharedContextProvider;
_singletonManager = singletonManager;
_trace = trace;
_loggerFactory = loggerFactory;
_converter = CreateConverter(_blobClient);
_bindingDataContract = CreateBindingDataContract(path);
}
@ -263,9 +267,9 @@ namespace Microsoft.Azure.WebJobs.Host.Blobs.Triggers
IStorageBlobContainer container = _blobClient.GetContainerReference(_path.ContainerNamePattern);
var factory = new BlobListenerFactory(_hostIdProvider, _queueConfiguration, _blobsConfiguration,
_exceptionHandler, _blobWrittenWatcherSetter, _messageEnqueuedWatcherSetter,
_sharedContextProvider, _trace, context.Descriptor.Id, _hostAccount, _dataAccount, container, _path, context.Executor, _singletonManager);
var factory = new BlobListenerFactory(_hostIdProvider, _queueConfiguration, _blobsConfiguration, _exceptionHandler,
_blobWrittenWatcherSetter, _messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, _loggerFactory,
context.Descriptor.Id, _hostAccount, _dataAccount, container, _path, context.Executor, _singletonManager);
return factory.CreateAsync(context.CancellationToken);
}

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Runtime.Serialization;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host
{
@ -71,7 +72,8 @@ namespace Microsoft.Azure.WebJobs.Host
/// Recovers if exception is handled by trace pipeline, else throws.
/// </summary>
/// <param name="trace"></param>
internal void TryRecover(TraceWriter trace)
/// <param name="logger"></param>
internal void TryRecover(TraceWriter trace, ILogger logger)
{
if (trace == null)
{
@ -79,6 +81,7 @@ namespace Microsoft.Azure.WebJobs.Host
}
trace.Error(Message, this);
logger?.LogError(0, this, Message);
if (!Handled)
{
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.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Executors
{
@ -26,12 +27,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
private readonly IFunctionOutputLogger _functionOutputLogger;
private readonly IWebJobsExceptionHandler _exceptionHandler;
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;
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)
{
@ -57,7 +61,9 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
_functionOutputLogger = functionOutputLogger;
_exceptionHandler = exceptionHandler;
_trace = trace;
_fastLogger = fastLogger;
_functionEventCollector = functionEventCollector;
_logger = loggerFactory?.CreateLogger(LogCategories.Executor);
_resultsLogger = loggerFactory?.CreateLogger(LogCategories.Results);
}
public HostOutputMessage HostOutputMessage
@ -84,9 +90,13 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
StartTime = functionStartedMessage.StartTime.DateTime
};
Stopwatch sw = Stopwatch.StartNew();
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);
}
catch (Exception exception)
@ -124,12 +134,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
logCompletedCancellationToken = cancellationToken;
}
if (_fastLogger != null)
{
// Log completed
fastItem.EndTime = DateTime.UtcNow;
fastItem.Arguments = functionCompletedMessage.Arguments;
// log result
sw.Stop();
fastItem.EndTime = DateTime.UtcNow;
fastItem.Duration = sw.Elapsed;
fastItem.Arguments = functionCompletedMessage.Arguments;
if (_functionEventCollector != null)
{
// Log completed
if (exceptionInfo != null)
{
var ex = exceptionInfo.SourceException;
@ -139,7 +152,11 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
}
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 &&
@ -229,16 +246,16 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
startedMessageId = await LogFunctionStartedAsync(message, outputDefinition, parameters, cancellationToken);
}
if (_fastLogger != null)
if (_functionEventCollector != null)
{
// Log started
fastItem.Arguments = message.Arguments;
await _fastLogger.AddAsync(fastItem);
await _functionEventCollector.AddAsync(fastItem);
}
try
{
await ExecuteWithLoggingAsync(instance, parameters, traceWriter, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource);
await ExecuteWithLoggingAsync(instance, parameters, traceWriter, _logger, outputDefinition, parameterLogCollector, functionTraceLevel, functionCancellationTokenSource);
}
catch (Exception ex)
{
@ -318,7 +335,8 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
/// create and start the timer.
/// </summary>
[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)
{
@ -348,7 +366,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
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);
};
@ -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,
TraceWriter trace, CancellationTokenSource cancellationTokenSource, Func<bool> isDebuggerAttached)
TraceWriter trace, ILogger logger, CancellationTokenSource cancellationTokenSource, Func<bool> isDebuggerAttached)
{
timer.Stop();
@ -372,6 +390,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
shouldTimeout ? "Initiating cancellation." : "Function will not be cancelled while debugging.");
trace.Error(message, null, TraceSource.Execution);
logger?.LogError(message);
trace.Flush();
@ -434,6 +453,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
private async Task ExecuteWithLoggingAsync(IFunctionInstance instance,
IReadOnlyDictionary<string, IValueProvider> parameters,
TraceWriter trace,
ILogger logger,
IFunctionOutputDefinition outputDefinition,
IDictionary<string, ParameterLog> parameterLogCollector,
TraceLevel functionTraceLevel,
@ -446,13 +466,13 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
if (functionTraceLevel >= TraceLevel.Info)
{
parameterWatchers = CreateParameterWatchers(parameters);
IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace);
IRecurrentCommand updateParameterLogCommand = outputDefinition.CreateParameterLogUpdateCommand(parameterWatchers, trace, logger);
updateParameterLogTimer = StartParameterLogTimer(updateParameterLogCommand, _exceptionHandler);
}
try
{
await ExecuteWithWatchersAsync(instance, parameters, trace, functionCancellationTokenSource);
await ExecuteWithWatchersAsync(instance, parameters, trace, logger, functionCancellationTokenSource);
if (updateParameterLogTimer != null)
{
@ -496,6 +516,7 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
internal static async Task ExecuteWithWatchersAsync(IFunctionInstance instance,
IReadOnlyDictionary<string, IValueProvider> parameters,
TraceWriter traceWriter,
ILogger logger,
CancellationTokenSource functionCancellationTokenSource)
{
IFunctionInvoker invoker = instance.Invoker;
@ -526,12 +547,12 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
MethodInfo method = instance.FunctionDescriptor.Method;
TimeoutAttribute timeoutAttribute = instance.FunctionDescriptor.TimeoutAttribute;
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);
try
{
await InvokeAsync(invoker, invokeParameters, timeoutTokenSource, functionCancellationTokenSource,
throwOnTimeout, timerInterval, instance);
throwOnTimeout, timerInterval, instance);
}
finally
{

Просмотреть файл

@ -26,6 +26,7 @@ using Microsoft.Azure.WebJobs.Host.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.Tables;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Executors
{
@ -34,9 +35,9 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
// Do full initialization (both static and runtime).
// This can be called multiple times on a config.
public static async Task<JobHostContext> CreateAndLogHostStartedAsync(
this JobHostConfiguration config,
JobHost host,
CancellationToken shutdownToken,
this JobHostConfiguration config,
JobHost host,
CancellationToken shutdownToken,
CancellationToken cancellationToken)
{
JobHostContext context = await config.CreateJobHostContextAsync(host, shutdownToken, cancellationToken);
@ -121,25 +122,25 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
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);
}
IExtensionRegistry extensions = services.GetExtensions();
ITriggerBindingProvider triggerBindingProvider = DefaultTriggerBindingProvider.Create(nameResolver,
storageAccountProvider, extensionTypeLocator, hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler,
messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, singletonManager, trace);
messageEnqueuedWatcherAccessor, blobWrittenWatcherAccessor, sharedContextProvider, extensions, singletonManager, trace, config.LoggerFactory);
services.AddService<ITriggerBindingProvider>(triggerBindingProvider);
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);
}
return services;
}
// Do the full runtime intitialization. This includes static initialization.
// This mainly means:
// - indexing the functions
@ -161,12 +162,34 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
INameResolver nameResolver = services.GetService<INameResolver>();
IExtensionRegistry extensions = services.GetExtensions();
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>();
var blobsConfiguration = config.Blobs;
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>();
if (exceptionHandler != null)
@ -209,25 +232,25 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
IStorageAccount dashboardAccount = await storageAccountProvider.GetDashboardAccountAsync(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);
if (functionExecutor == null)
{
functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, fastLogger);
functionExecutor = new FunctionExecutor(functionInstanceLogger, functionOutputLogger, exceptionHandler, trace, functionEventCollector, loggerFactory);
services.AddService(functionExecutor);
}
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.
services.AddService<IFunctionIndexProvider>(functionIndexProvider);
}
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;
IListener listener;
@ -257,14 +280,14 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
IStorageQueueClient dashboardQueueClient = dashboardAccount.CreateQueueClient();
IStorageQueue sharedQueue = dashboardQueueClient.GetQueueReference(sharedQueueName);
IListenerFactory sharedQueueListenerFactory = new HostMessageListenerFactory(sharedQueue,
queueConfiguration, exceptionHandler, trace, functions,
queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
functionInstanceLogger, functionExecutor);
Guid hostInstanceId = Guid.NewGuid();
string instanceQueueName = HostQueueNames.GetHostQueueName(hostInstanceId.ToString("N"));
IStorageQueue instanceQueue = dashboardQueueClient.GetQueueReference(instanceQueueName);
IListenerFactory instanceQueueListenerFactory = new HostMessageListenerFactory(instanceQueue,
queueConfiguration, exceptionHandler, trace, functions,
queueConfiguration, exceptionHandler, trace, loggerFactory, functions,
functionInstanceLogger, functionExecutor);
HeartbeatDescriptor heartbeatDescriptor = new HeartbeatDescriptor
@ -309,15 +332,22 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
IEnumerable<FunctionDescriptor> descriptors = functions.ReadAllDescriptors();
int descriptorsCount = descriptors.Count();
ILogger startupLogger = loggerFactory?.CreateLogger(LogCategories.Startup);
if (config.UsingDevelopmentSettings)
{
trace.Verbose(string.Format("Development settings applied"));
string msg = "Development settings applied";
trace.Verbose(msg);
startupLogger?.LogDebug(msg);
}
if (descriptorsCount == 0)
{
trace.Warning(string.Format("No job functions found. Try making your job classes and methods public. {0}",
Constants.ExtensionInitializationMessage), Host.TraceSource.Indexing);
string msg = string.Format("No job functions found. Try making your job classes and methods public. {0}",
Constants.ExtensionInitializationMessage);
trace.Warning(msg, Host.TraceSource.Indexing);
startupLogger?.LogWarning(msg);
}
else
{
@ -328,11 +358,12 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
{
functionsTrace.AppendLine(descriptor.FullName);
}
trace.Info(functionsTrace.ToString(), Host.TraceSource.Indexing);
string msg = functionsTrace.ToString();
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.Listeners;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Executors
{
@ -16,7 +17,8 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
private readonly IFunctionExecutor _executor;
private readonly IListener _listener;
private readonly TraceWriter _trace;
private readonly IAsyncCollector<FunctionInstanceLogEntry> _fastLogger; // optional
private readonly IAsyncCollector<FunctionInstanceLogEntry> _functionEventCollector; // optional
private readonly ILoggerFactory _loggerFactory;
private bool _disposed;
@ -24,13 +26,15 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
IFunctionExecutor executor,
IListener listener,
TraceWriter trace,
IAsyncCollector<FunctionInstanceLogEntry> fastLogger = null)
IAsyncCollector<FunctionInstanceLogEntry> functionEventCollector = null,
ILoggerFactory loggerFactory = null)
{
_functionLookup = functionLookup;
_executor = executor;
_listener = listener;
_trace = trace;
_fastLogger = fastLogger;
_functionEventCollector = functionEventCollector;
_loggerFactory = loggerFactory;
}
public TraceWriter Trace
@ -69,12 +73,21 @@ namespace Microsoft.Azure.WebJobs.Host.Executors
}
}
public IAsyncCollector<FunctionInstanceLogEntry> FastLogger
public IAsyncCollector<FunctionInstanceLogEntry> FunctionEventCollector
{
get
{
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.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.Bindings;
using Microsoft.Azure.WebJobs.Host.Tables;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Indexers
{
internal static class DefaultBindingProvider
{
public static IBindingProvider Create(
INameResolver nameResolver,
INameResolver nameResolver,
ILoggerFactory loggerFactory,
IStorageAccountProvider storageAccountProvider,
IExtensionTypeLocator extensionTypeLocator,
IContextGetter<IBlobWrittenWatcher> blobWrittenWatcherGetter,
IExtensionRegistry extensions)
IExtensionRegistry extensions)
{
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
// 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>();
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.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Indexers
{
@ -27,15 +28,16 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
ISharedContextProvider sharedContextProvider,
IExtensionRegistry extensions,
SingletonManager singletonManager,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
List<ITriggerBindingProvider> innerProviders = new List<ITriggerBindingProvider>();
innerProviders.Add(new QueueTriggerAttributeBindingProvider(nameResolver, storageAccountProvider,
queueConfiguration, exceptionHandler, messageEnqueuedWatcherSetter,
sharedContextProvider, trace));
innerProviders.Add(new BlobTriggerAttributeBindingProvider(nameResolver, storageAccountProvider,
extensionTypeLocator, hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler,
blobWrittenWatcherSetter, messageEnqueuedWatcherSetter, sharedContextProvider, singletonManager, trace));
sharedContextProvider, trace, loggerFactory));
innerProviders.Add(new BlobTriggerAttributeBindingProvider(nameResolver, storageAccountProvider, extensionTypeLocator,
hostIdProvider, queueConfiguration, blobsConfiguration, exceptionHandler, blobWrittenWatcherSetter,
messageEnqueuedWatcherSetter, sharedContextProvider, singletonManager, trace, loggerFactory));
// add any registered extension binding providers
foreach (ITriggerBindingProvider provider in extensions.GetExtensions(typeof(ITriggerBindingProvider)))

Просмотреть файл

@ -7,8 +7,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Indexers
{
@ -22,6 +22,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
private readonly IExtensionRegistry _extensions;
private readonly SingletonManager _singletonManager;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private IFunctionIndex _index;
@ -32,7 +33,8 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
IFunctionExecutor executor,
IExtensionRegistry extensions,
SingletonManager singletonManager,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
if (typeLocator == null)
{
@ -82,6 +84,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
_extensions = extensions;
_singletonManager = singletonManager;
_trace = trace;
_loggerFactory = loggerFactory;
}
public async Task<IFunctionIndex> GetAsync(CancellationToken cancellationToken)
@ -97,7 +100,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
private async Task<IFunctionIndex> CreateAsync(CancellationToken cancellationToken)
{
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();
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.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Indexers
{
@ -28,8 +30,10 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
private readonly HashSet<Assembly> _jobAttributeAssemblies;
private readonly SingletonManager _singletonManager;
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)
{
@ -73,6 +77,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
_singletonManager = singletonManager;
_jobAttributeAssemblies = GetJobAttributeAssemblies(extensions);
_trace = trace;
_logger = loggerFactory?.CreateLogger(LogCategories.Startup);
}
public async Task IndexTypeAsync(Type type, IFunctionIndexCollector index, CancellationToken cancellationToken)
@ -85,7 +90,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
}
catch (FunctionIndexingException fex)
{
fex.TryRecover(_trace);
fex.TryRecover(_trace, _logger);
// If recoverable, continue to the rest of the methods.
// The method in error simply won't be running in the JobHost.
continue;
@ -123,7 +128,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
// create a set containing our own core assemblies
HashSet<Assembly> assemblies = new HashSet<Assembly>();
assemblies.Add(typeof(BlobAttribute).Assembly);
// add any extension assemblies
assemblies.UnionWith(extensions.GetExtensionAssemblies());
@ -206,7 +211,7 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
if (triggerBinding != null && !hasNoAutomaticTriggerAttribute)
{
throw new InvalidOperationException(
string.Format(Constants.UnableToBindParameterFormat,
string.Format(Constants.UnableToBindParameterFormat,
parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage));
}
else
@ -246,7 +251,9 @@ namespace Microsoft.Azure.WebJobs.Host.Indexers
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;

Просмотреть файл

@ -12,7 +12,9 @@ using Microsoft.Azure.WebJobs.Host;
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.Protocols;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
namespace Microsoft.Azure.WebJobs
@ -36,7 +38,7 @@ namespace Microsoft.Azure.WebJobs
private Task<JobHostContext> _contextTask;
private bool _contextTaskInitialized;
private object _contextTaskLock = new object();
private JobHostContext _context;
private IListener _listener;
private object _contextLock = new object();
@ -46,6 +48,8 @@ namespace Microsoft.Azure.WebJobs
private object _stopTaskLock = new object();
private bool _disposed;
private ILogger _logger;
/// <summary>
/// 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.
@ -114,7 +118,10 @@ namespace Microsoft.Azure.WebJobs
await EnsureHostStartedAsync(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;
}
@ -156,13 +163,15 @@ namespace Microsoft.Azure.WebJobs
await _listener.StopAsync(cancellationToken);
// Flush remaining logs
var fastLogger = _context.FastLogger;
if (fastLogger != null)
var functionEventCollector = _context.FunctionEventCollector;
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>
@ -321,7 +330,7 @@ namespace Microsoft.Azure.WebJobs
}
private async Task<JobHostContext> CreateContextAndLogHostStartedAsync(CancellationToken cancellationToken)
{
{
JobHostContext context = await _config.CreateAndLogHostStartedAsync(this, _shutdownTokenSource.Token, cancellationToken);
lock (_contextLock)
@ -333,6 +342,8 @@ namespace Microsoft.Azure.WebJobs
}
}
_logger = _context.LoggerFactory?.CreateLogger(LogCategories.Startup);
return _context;
}

Просмотреть файл

@ -13,6 +13,7 @@ using Microsoft.Azure.WebJobs.Host.Indexers;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Queues;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs
@ -43,7 +44,7 @@ namespace Microsoft.Azure.WebJobs
: this(null)
{
}
/// <summary>
/// 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.
@ -62,6 +63,7 @@ namespace Microsoft.Azure.WebJobs
}
Singleton = new SingletonConfiguration();
Aggregator = new FunctionResultAggregatorConfiguration();
// add our built in services here
_tooling = new JobHostMetadataProvider(this);
@ -80,9 +82,10 @@ namespace Microsoft.Azure.WebJobs
AddService<ITypeLocator>(typeLocator);
AddService<IConverterManager>(converterManager);
AddService<IWebJobsExceptionHandler>(exceptionHandler);
AddService<IFunctionResultAggregatorFactory>(new FunctionResultAggregatorFactory());
string value = ConfigurationUtility.GetSettingFromConfigOrEnvironment(Constants.EnvironmentSettingName);
IsDevelopment = string.Compare(Constants.DevelopmentEnvironmentValue, value, StringComparison.OrdinalIgnoreCase) == 0;
string value = ConfigurationUtility.GetSettingFromConfigOrEnvironment(Host.Constants.EnvironmentSettingName);
IsDevelopment = string.Compare(Host.Constants.DevelopmentEnvironmentValue, value, StringComparison.OrdinalIgnoreCase) == 0;
}
/// <summary>
@ -257,6 +260,31 @@ namespace Microsoft.Azure.WebJobs
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>
/// Gets the configuration for event tracing.
/// </summary>
@ -353,10 +381,10 @@ namespace Microsoft.Azure.WebJobs
}
_services.AddOrUpdate(serviceType, serviceInstance, (key, existingValue) =>
{
// always replace existing values
return serviceInstance;
});
{
// always replace existing values
return serviceInstance;
});
}
/// <summary>
@ -383,7 +411,7 @@ namespace Microsoft.Azure.WebJobs
{
_tooling.AddAttributesFromAssembly(assembly);
}
/// <summary>
/// Get a tooling interface for inspecting current extensions.
/// </summary>
@ -418,4 +446,4 @@ namespace Microsoft.Azure.WebJobs
}
}
}
}
}

Просмотреть файл

@ -4,7 +4,9 @@
using System;
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.Listeners
{
@ -13,6 +15,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
private readonly IListener _listener;
private readonly FunctionDescriptor _descriptor;
private readonly TraceWriter _trace;
private readonly ILogger _logger;
private bool _started = false;
/// <summary>
@ -22,11 +25,13 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
/// <param name="listener"></param>
/// <param name="descriptor"></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;
_descriptor = descriptor;
_trace = trace;
_logger = loggerFactory?.CreateLogger(LogCategories.Startup);
}
public void Cancel()
@ -48,10 +53,10 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
}
catch (Exception e)
{
new FunctionListenerException(_descriptor.ShortName, e).TryRecover(_trace);
new FunctionListenerException(_descriptor.ShortName, e).TryRecover(_trace, _logger);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (_started)

Просмотреть файл

@ -10,6 +10,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings.Path;
using Microsoft.Azure.WebJobs.Host.Indexers;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Listeners
{
@ -22,14 +24,19 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
private readonly IJobActivator _activator;
private readonly INameResolver _nameResolver;
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;
_singletonManager = singletonManager;
_activator = activator;
_nameResolver = nameResolver;
_trace = trace;
_loggerFactory = loggerFactory;
_logger = _loggerFactory?.CreateLogger(LogCategories.Startup);
}
public async Task<IListener> CreateAsync(CancellationToken cancellationToken)
@ -48,7 +55,9 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
MethodInfo method = functionDefinition.Descriptor.Method;
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;
}
@ -58,11 +67,11 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
SingletonAttribute singletonAttribute = SingletonManager.GetListenerSingletonOrNull(listener.GetType(), method);
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
listener = new FunctionListener(listener, functionDefinition.Descriptor, _trace);
listener = new FunctionListener(listener, functionDefinition.Descriptor, _trace, _loggerFactory);
listeners.Add(listener);
}
@ -134,7 +143,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
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));
}

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
namespace Microsoft.Azure.WebJobs.Host.Loggers
@ -46,9 +47,9 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
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)

Просмотреть файл

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Loggers
{
@ -31,7 +32,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
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;
}

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Loggers
{
@ -102,7 +103,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
return this;
}
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace)
public IRecurrentCommand CreateParameterLogUpdateCommand(IReadOnlyDictionary<string, IWatcher> watches, TraceWriter trace, ILogger logger)
{
return null;
}

Просмотреть файл

@ -3,9 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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>
public DateTime? EndTime { get; set; }
/// <summary>
/// The duration of the function execution.
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Null on success.
/// Else, set to some string with error details.
@ -48,7 +50,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
public string ErrorDetails { get; set; }
/// <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; }
/// <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.
using System.Collections.Generic;
using System.Threading;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Loggers
{
@ -17,6 +17,6 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
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.Storage.Blob;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Microsoft.Azure.WebJobs.Host.Loggers
@ -18,11 +19,12 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
private readonly IReadOnlyDictionary<string, IWatcher> _watches;
private readonly IStorageBlockBlob _parameterLogBlob;
private readonly TraceWriter _trace;
private readonly ILogger _logger;
private string _lastContent;
public UpdateParameterLogCommand(IReadOnlyDictionary<string, IWatcher> watches,
IStorageBlockBlob parameterLogBlob, TraceWriter trace)
IStorageBlockBlob parameterLogBlob, TraceWriter trace, ILogger logger)
{
if (parameterLogBlob == null)
{
@ -39,6 +41,7 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
_parameterLogBlob = parameterLogBlob;
_trace = trace;
_logger = logger;
_watches = watches;
}
@ -91,7 +94,9 @@ namespace Microsoft.Azure.WebJobs.Host.Loggers
{
// Not fatal if we can't update parameter status.
// 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;
}
}

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
{
@ -19,6 +20,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
private readonly IQueueConfiguration _queueConfiguration;
private readonly IWebJobsExceptionHandler _exceptionHandler;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private readonly IFunctionIndexLookup _functionLookup;
private readonly IFunctionInstanceLogger _functionInstanceLogger;
private readonly IFunctionExecutor _executor;
@ -27,6 +29,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
IQueueConfiguration queueConfiguration,
IWebJobsExceptionHandler exceptionHandler,
TraceWriter trace,
ILoggerFactory loggerFactory,
IFunctionIndexLookup functionLookup,
IFunctionInstanceLogger functionInstanceLogger,
IFunctionExecutor executor)
@ -70,6 +73,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
_queueConfiguration = queueConfiguration;
_exceptionHandler = exceptionHandler;
_trace = trace;
_loggerFactory = loggerFactory;
_functionLookup = functionLookup;
_functionInstanceLogger = functionInstanceLogger;
_executor = executor;
@ -89,6 +93,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
triggerExecutor: triggerExecutor,
exceptionHandler: _exceptionHandler,
trace: _trace,
loggerFactory: _loggerFactory,
sharedWatcher: null,
queueConfiguration: _queueConfiguration,
maxPollingInterval: maxPollingInterval);

Просмотреть файл

@ -11,6 +11,7 @@ using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
@ -24,7 +25,6 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
private readonly IStorageQueue _poisonQueue;
private readonly ITriggerExecutor<IStorageQueueMessage> _triggerExecutor;
private readonly IWebJobsExceptionHandler _exceptionHandler;
private readonly TraceWriter _trace;
private readonly IMessageEnqueuedWatcher _sharedWatcher;
private readonly List<Task> _processing = new List<Task>();
private readonly object _stopWaitingTaskSourceLock = new object();
@ -41,6 +41,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
ITriggerExecutor<IStorageQueueMessage> triggerExecutor,
IWebJobsExceptionHandler exceptionHandler,
TraceWriter trace,
ILoggerFactory loggerFactory,
SharedQueueWatcher sharedWatcher,
IQueueConfiguration queueConfiguration,
QueueProcessor queueProcessor = null,
@ -71,7 +72,6 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
_poisonQueue = poisonQueue;
_triggerExecutor = triggerExecutor;
_exceptionHandler = exceptionHandler;
_trace = trace;
_queueConfiguration = queueConfiguration;
// 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;
_queueProcessor = queueProcessor ?? CreateQueueProcessor(
_queue.SdkObject, _poisonQueue != null ? _poisonQueue.SdkObject : null,
_trace, _queueConfiguration, poisonMessageEventHandler);
trace, loggerFactory, _queueConfiguration, poisonMessageEventHandler);
TimeSpan maximumInterval = _queueProcessor.MaxPollingInterval;
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;
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.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
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 ISharedContextProvider _sharedContextProvider;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private readonly ITriggeredFunctionExecutor _executor;
public QueueListenerFactory(IStorageQueue queue,
@ -31,6 +33,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
TraceWriter trace,
ILoggerFactory loggerFactory,
ITriggeredFunctionExecutor executor)
{
if (queue == null)
@ -75,6 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
_sharedContextProvider = sharedContextProvider;
_trace = trace;
_loggerFactory = loggerFactory;
_executor = executor;
}
@ -87,7 +91,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Listeners
new SharedQueueWatcherFactory(_messageEnqueuedWatcherSetter));
IListener listener = new QueueListener(_queue, _poisonQueue, triggerExecutor,
_exceptionHandler, _trace, sharedWatcher, _queueConfiguration);
_exceptionHandler, _trace, _loggerFactory, sharedWatcher, _queueConfiguration);
return Task.FromResult(listener);
}

Просмотреть файл

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
@ -26,6 +27,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
private readonly CloudQueue _queue;
private readonly CloudQueue _poisonQueue;
private readonly TraceWriter _trace;
private readonly ILogger _logger;
/// <summary>
/// Constructs a new instance.
@ -41,6 +43,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
_queue = context.Queue;
_poisonQueue = context.PoisonQueue;
_trace = context.Trace;
_logger = context.Logger;
MaxDequeueCount = context.MaxDequeueCount;
BatchSize = context.BatchSize;
@ -106,7 +109,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param>
/// <returns></returns>
public virtual async Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken)
{
{
if (result.Succeeded)
{
await DeleteMessageAsync(message, cancellationToken);
@ -140,7 +143,9 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
/// <returns></returns>
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);

Просмотреть файл

@ -2,6 +2,8 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
namespace Microsoft.Azure.WebJobs.Host.Queues
@ -17,8 +19,9 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
/// </summary>
/// <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="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>
public QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, CloudQueue poisonQueue = null)
public QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, ILoggerFactory loggerFactory, CloudQueue poisonQueue = null)
{
if (queue == null)
{
@ -32,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
Queue = queue;
PoisonQueue = poisonQueue;
Trace = trace;
Logger = loggerFactory?.CreateLogger(LogCategories.CreateTriggerCategory("Queue"));
}
/// <summary>
@ -39,10 +43,11 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
/// </summary>
/// <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="loggerFactory">The <see cref="ILoggerFactory"/> to create an <see cref="ILogger"/> from.</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>
internal QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, IQueueConfiguration queueConfiguration, CloudQueue poisonQueue = null)
: this(queue, trace, poisonQueue)
internal QueueProcessorFactoryContext(CloudQueue queue, TraceWriter trace, ILoggerFactory loggerFactory, IQueueConfiguration queueConfiguration, CloudQueue poisonQueue = null)
: this(queue, trace, loggerFactory, poisonQueue)
{
BatchSize = queueConfiguration.BatchSize;
MaxDequeueCount = queueConfiguration.MaxDequeueCount;
@ -67,6 +72,11 @@ namespace Microsoft.Azure.WebJobs.Host.Queues
/// </summary>
public TraceWriter Trace { get; private set; }
/// <summary>
/// Gets the <see cref="ILogger"/>.
/// </summary>
public ILogger Logger { get; private set; }
/// <summary>
/// Gets or sets the number of queue messages to retrieve and process in parallel (per job method).
/// </summary>

Просмотреть файл

@ -10,6 +10,7 @@ using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Azure.WebJobs.Host.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
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 ISharedContextProvider _sharedContextProvider;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
public QueueTriggerAttributeBindingProvider(INameResolver nameResolver,
IStorageAccountProvider accountProvider,
@ -38,7 +40,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
IWebJobsExceptionHandler exceptionHandler,
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
if (accountProvider == null)
{
@ -77,6 +80,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
_sharedContextProvider = sharedContextProvider;
_trace = trace;
_loggerFactory = loggerFactory;
}
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,
_queueConfiguration, _exceptionHandler, _messageEnqueuedWatcherSetter,
_sharedContextProvider, _trace);
_sharedContextProvider, _trace, _loggerFactory);
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.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
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 ISharedContextProvider _sharedContextProvider;
private readonly TraceWriter _trace;
private readonly ILoggerFactory _loggerFactory;
private readonly IObjectToTypeConverter<IStorageQueueMessage> _converter;
public QueueTriggerBinding(string parameterName,
@ -36,7 +38,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
IWebJobsExceptionHandler exceptionHandler,
IContextSetter<IMessageEnqueuedWatcher> messageEnqueuedWatcherSetter,
ISharedContextProvider sharedContextProvider,
TraceWriter trace)
TraceWriter trace,
ILoggerFactory loggerFactory)
{
if (queue == null)
{
@ -82,6 +85,7 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
_messageEnqueuedWatcherSetter = messageEnqueuedWatcherSetter;
_sharedContextProvider = sharedContextProvider;
_trace = trace;
_loggerFactory = loggerFactory;
_converter = CreateConverter(queue);
}
@ -156,8 +160,8 @@ namespace Microsoft.Azure.WebJobs.Host.Queues.Triggers
throw new ArgumentNullException("context");
}
var factory = new QueueListenerFactory(_queue, _queueConfiguration, _exceptionHandler,
_messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, context.Executor);
var factory = new QueueListenerFactory(_queue, _queueConfiguration, _exceptionHandler,
_messageEnqueuedWatcherSetter, _sharedContextProvider, _trace, _loggerFactory, context.Executor);
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("NextVisibleTime", value.NextVisibleTime.GetValueOrDefault(DateTimeOffset.MaxValue));
bindingData.Add("PopReceipt", value.PopReceipt);
if (bindingDataFromValueType != null)
{
foreach (KeyValuePair<string, object> item in bindingDataFromValueType)

Просмотреть файл

@ -7,6 +7,8 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.Listeners
{
@ -17,11 +19,12 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
private readonly SingletonConfiguration _singletonConfig;
private readonly IListener _innerListener;
private readonly TraceWriter _trace;
private readonly ILogger _logger;
private string _lockId;
private object _lockHandle;
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;
_singletonManager = singletonManager;
@ -33,6 +36,7 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
_lockId += ".Listener";
_trace = trace;
_logger = loggerFactory?.CreateLogger(LogCategories.Singleton);
}
// exposed for testing
@ -47,7 +51,9 @@ namespace Microsoft.Azure.WebJobs.Host.Listeners
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
// 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.Executors;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Storage;
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
@ -33,6 +35,8 @@ namespace Microsoft.Azure.WebJobs.Host
private readonly IWebJobsExceptionHandler _exceptionHandler;
private readonly SingletonConfiguration _config;
private readonly IStorageAccountProvider _accountProvider;
private readonly ILogger _logger;
private readonly ILoggerFactory _loggerFactory;
private ConcurrentDictionary<string, IStorageBlobDirectory> _lockDirectoryMap = new ConcurrentDictionary<string, IStorageBlobDirectory>(StringComparer.OrdinalIgnoreCase);
private TimeSpan _minimumLeaseRenewalInterval = TimeSpan.FromSeconds(1);
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;
_nameResolver = nameResolver;
_exceptionHandler = exceptionHandler;
_config = config;
_trace = trace;
_loggerFactory = loggerFactory;
_logger = _loggerFactory?.CreateLogger(LogCategories.Singleton);
_hostIdProvider = hostIdProvider;
}
@ -130,7 +137,9 @@ namespace Microsoft.Azure.WebJobs.Host
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))
{
@ -163,7 +172,9 @@ namespace Microsoft.Azure.WebJobs.Host
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)
@ -251,7 +262,7 @@ namespace Microsoft.Azure.WebJobs.Host
{
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)
@ -351,7 +362,7 @@ namespace Microsoft.Azure.WebJobs.Host
TimeSpan normalUpdateInterval = new TimeSpan(leasePeriod.Ticks / 2);
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));
}
@ -550,11 +561,13 @@ namespace Microsoft.Azure.WebJobs.Host
private readonly string _lockId;
private readonly IDelayStrategy _speedupStrategy;
private readonly TraceWriter _trace;
private readonly ILogger _logger;
private DateTimeOffset _lastRenewal;
private TimeSpan _lastRenewalLatency;
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;
_leaseBlob = leaseBlob;
@ -562,6 +575,7 @@ namespace Microsoft.Azure.WebJobs.Host
_lockId = lockId;
_speedupStrategy = speedupStrategy;
_trace = trace;
_logger = logger;
_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).
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.",
_lockId, FormatErrorCode(exception), delay.TotalMilliseconds), source: TraceSource.Execution);
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);
_trace.Warning(msg, source: TraceSource.Execution);
_logger?.LogWarning(msg);
}
else
{
@ -600,8 +616,10 @@ namespace Microsoft.Azure.WebJobs.Host
int millisecondsSinceLastSuccess = (int)(DateTime.UtcNow - _lastRenewal).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.",
_lockId, FormatErrorCode(exception), lastRenewalFormatted, millisecondsSinceLastSuccess, lastRenewalMilliseconds, leasePeriodMilliseconds));
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);
_trace.Error(msg);
_logger?.LogError(msg);
// If we've lost the lease or cannot re-establish it, we want to fail any
// in progress function execution

Просмотреть файл

@ -54,6 +54,30 @@
<AssemblyOriginatorKeyFile>..\Common\PublicKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<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">
<HintPath>..\..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
<Private>True</Private>
@ -70,6 +94,14 @@
<HintPath>..\..\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.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">
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
<Private>True</Private>
@ -83,19 +115,90 @@
<Private>True</Private>
</Reference>
<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.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.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.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.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>
<Private>True</Private>
</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="Microsoft.CSharp" />
<Reference Include="System.Data" />
<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>
<Compile Include="..\Common\CommonAssemblyInfo.cs">
@ -393,6 +496,8 @@
<Compile Include="Bindings\DefaultAttributeInvokerDescriptor.cs" />
<Compile Include="Bindings\FuncArgumentBuilder.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\Path\BindingParameterResolver.cs" />
<Compile Include="Bindings\PatternMatcher.cs" />
@ -408,14 +513,36 @@
<Compile Include="Config\ServiceProviderWrapper.cs" />
<Compile Include="DefaultNameResolver.cs" />
<Compile Include="Diagnostics\ExceptionFormatter.cs" />
<Compile Include="Executors\CompositeFunctionEventCollector.cs" />
<Compile Include="Executors\FunctionInstanceTraceWriter.cs" />
<Compile Include="Extensions\IJobHostMetadataProvider.cs" />
<Compile Include="Extensions\JobHostMetadataProvider.cs" />
<Compile Include="Extensions\DictionaryExtensions.cs" />
<Compile Include="FuncConverter.cs" />
<Compile Include="FunctionTimeoutException.cs" />
<Compile Include="Exceptions\RecoverableException.cs" />
<Compile Include="JobHostBlobsConfiguration.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="Exceptions\FunctionException.cs" />
<Compile Include="Queues\Bindings\QueueBindingProvider.cs" />

Просмотреть файл

@ -6,5 +6,7 @@ namespace Microsoft.Azure.WebJobs.Host
internal static class WebSitesKnownKeyNames
{
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>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
</assemblyBinding>
</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"?>
<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.Data.Edm" 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.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="NETStandard.Library" version="1.6.1" targetFramework="net46" />
<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="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.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.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.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.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.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="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
</packages>

Просмотреть файл

@ -17,6 +17,8 @@
<dependency id="Microsoft.Azure.WebJobs.Core" version="[$WebJobsPackageVersion$]" />
<dependency id="Newtonsoft.Json" version="9.0.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>
</metadata>
</package>

Просмотреть файл

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0"/>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
@ -32,30 +32,30 @@
<extensions>
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
<behaviorExtensions>
<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="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, 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="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
<bindingElementExtensions>
<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="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="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, 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="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="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingElementExtensions>
<bindingExtensions>
<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="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="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="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, 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="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="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="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingExtensions>
</extensions>
</system.serviceModel>
<appSettings>
<!-- 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>
<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.Configuration" />
<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">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
<Private>True</Private>

Просмотреть файл

@ -1,52 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="TestConnection1" connectionString="Test Connection 1"/>
<add name="TestConnection2" connectionString="Test Connection 2"/>
<add name="TestConnection1" connectionString="Test Connection 1" />
<add name="TestConnection2" connectionString="Test Connection 2" />
</connectionStrings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0"/>
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234"/>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0"/>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</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>
</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>
<appSettings>
<add key="Disable_DisabledJob" value="1"/>
<add key="Disable_DisabledJob" value="1" />
</appSettings>
<connectionStrings>
<!-- CS format: DefaultEndpointsProtocol=https;AccountName=[ACCOUNT];AccountKey=[KEY] -->
<add name="AzureWebJobsDashboard" connectionString=""/>
<add name="AzureWebJobsStorage" connectionString=""/>
<add name="AzureWebJobsSecondaryStorage" connectionString=""/>
<add name="AzureWebJobsDashboard" connectionString="" />
<add name="AzureWebJobsStorage" connectionString="" />
<add name="AzureWebJobsSecondaryStorage" connectionString="" />
<add name="AzureWebJobsServiceBus" connectionString=""/>
<add name="AzureWebJobsServiceBus" connectionString="" />
</connectionStrings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0"/>
<assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.6.0.0" newVersion="5.6.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
<assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0"/>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.2.1.0" newVersion="7.2.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="xunit.core" publicKeyToken="8d05b1bb7a6fdb6c" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.0.2929" newVersion="2.0.0.2929"/>
<assemblyIdentity name="xunit.core" publicKeyToken="8d05b1bb7a6fdb6c" culture="neutral" />
<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>
</assemblyBinding>
</runtime>
@ -51,26 +55,26 @@
<extensions>
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
<behaviorExtensions>
<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="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, 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="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
<bindingElementExtensions>
<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="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="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, 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="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="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingElementExtensions>
<bindingExtensions>
<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="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="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="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, 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="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="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="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingExtensions>
</extensions>
</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.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Queues;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
@ -51,6 +53,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
private readonly CloudQueue _testQueue;
private readonly TestFixture _fixture;
private readonly TestLoggerProvider _loggerProvider = new TestLoggerProvider();
public AsyncChainEndToEndTests(TestFixture fixture)
{
_fixture = fixture;
@ -66,6 +70,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
_storageAccount = fixture.StorageAccount;
_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();
string queueName = _resolver.ResolveInString(TestQueueName);
_testQueue = queueClient.GetQueueReference(queueName);
@ -86,6 +95,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
await AsyncChainEndToEndInternal();
Console.SetOut(hold);
string firstQueueName = _resolver.ResolveInString(Queue1Name);
string secondQueueName = _resolver.ResolveInString(Queue2Name);
string blobContainerName = _resolver.ResolveInString(ContainerName);
@ -124,15 +135,23 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
}.OrderBy(p => p).ToArray();
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();
bool hasError = string.Join(Environment.NewLine, trace.Traces.Where(p => p.Message.Contains("Error"))).Any();
if (!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);
Assert.False(hasError);
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")));
}
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(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);
}
[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)
{
foreach (var traceEvent in trace.Traces)
@ -251,17 +378,28 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
}
catch { }
string expectedName = $"{methodInfo.DeclaringType.FullName}.{methodInfo.Name}";
// Validate TraceWriter
// We expect 3 error messages total
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
Assert.Equal(3, traceErrors.Length);
// Ensure that all errors include the same exception, with function
// invocation details
// invocation details
FunctionInvocationException functionException = traceErrors.First().Exception as FunctionInvocationException;
Assert.NotNull(functionException);
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));
// 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]
@ -349,12 +487,24 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
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
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
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.True(traceErrors[1].Message.StartsWith(string.Format("Executed 'AsyncChainEndToEndTests.{0}' (Failed, Id=", functionName)));
Assert.True(traceErrors[2].Message.Trim().StartsWith("Function had errors. See Azure WebJobs SDK dashboard for details."));
Assert.StartsWith(expectedExceptionMessage, traceErrors[0].Message);
Assert.StartsWith(expectedResultMessage, traceErrors[1].Message);
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]
@ -368,8 +518,13 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
MethodInfo methodInfo = GetType().GetMethod("TimeoutJob");
await host.CallAsync(methodInfo);
// Validate TraceWriter
TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
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]
@ -781,5 +936,21 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
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.Tasks;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
@ -335,6 +336,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
var tracer = new TestTraceWriter(TraceLevel.Verbose);
hostConfig.Tracing.Tracers.Add(tracer);
ILoggerFactory loggerFactory = new LoggerFactory();
TestLoggerProvider loggerProvider = new TestLoggerProvider();
loggerFactory.AddProvider(loggerProvider);
hostConfig.LoggerFactory = loggerFactory;
// The jobs host is started
JobHost host = new JobHost(hostConfig);
host.Start();
@ -385,6 +391,10 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
// make sure the exception is being properly logged
var errors = tracer.Traces.Where(t => t.Level == TraceLevel.Error);
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()

Просмотреть файл

@ -14,6 +14,9 @@
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<CodeAnalysisRuleSet>..\test.ruleset</CodeAnalysisRuleSet>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>
<TargetFrameworkProfile />
</PropertyGroup>
<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>
<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.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>
<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">
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
<Private>True</Private>
@ -71,9 +90,72 @@
<Private>True</Private>
</Reference>
<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.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.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.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.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>
@ -81,6 +163,10 @@
</Reference>
<Reference Include="System.Xml" />
<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">
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>

Просмотреть файл

@ -4,13 +4,63 @@
<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.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="NETStandard.Library" version="1.6.1" targetFramework="net46" />
<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.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.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.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.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.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.Storage" version="7.2.1" 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.Storage.Queue;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
using Moq;
using Xunit;
@ -31,7 +32,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
_poisonQueue = fixture.PoisonQueue;
_queuesConfig = new JobHostQueuesConfiguration();
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, _queuesConfig);
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, _queuesConfig);
_processor = new QueueProcessor(context);
}
@ -46,7 +47,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
VisibilityTimeout = TimeSpan.FromSeconds(30),
MaxPollingInterval = TimeSpan.FromSeconds(15)
};
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, config);
QueueProcessorFactoryContext context = new QueueProcessorFactoryContext(_queue, _trace, null, config);
QueueProcessor localProcessor = new QueueProcessor(context);
Assert.Equal(config.BatchSize, localProcessor.BatchSize);
@ -94,7 +95,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
[Fact]
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);
bool poisonMessageHandlerCalled = false;
@ -134,7 +135,7 @@ namespace Microsoft.Azure.WebJobs.Host.FunctionalTests
// configure a non-zero visibility timeout
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);
string messageContent = Guid.NewGuid().ToString();

Просмотреть файл

@ -59,6 +59,7 @@
<Compile Include="Blobs\Listeners\ScanContainersStrategyTests.cs" />
<Compile Include="Blobs\Listeners\StorageBlobScanInfoManagerTests.cs" />
<Compile Include="BlobTriggerTests.cs" />
<Compile Include="ILoggerTests.cs" />
<Compile Include="JobHostTests.cs" />
<Compile Include="FunctionalTest.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>
<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.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">
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
<Private>True</Private>
@ -162,14 +179,81 @@
<Private>True</Private>
</Reference>
<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.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.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">
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml" />
<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">
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>

Просмотреть файл

@ -1,25 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<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>
</assemblyBinding>
</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.OData" 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="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="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.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.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.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.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.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="xunit" version="2.1.0" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />

Просмотреть файл

@ -1,15 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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.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;
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.Protocols;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.TestCommon
{
@ -47,7 +48,7 @@ namespace Microsoft.Azure.WebJobs.Host.TestCommon
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;
}

Просмотреть файл

@ -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>
<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.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">
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
<Private>True</Private>
@ -79,7 +95,70 @@
<Private>True</Private>
</Reference>
<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.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">
<HintPath>..\..\packages\System.Spatial.5.8.2\lib\net40\System.Spatial.dll</HintPath>
<Private>True</Private>
@ -89,6 +168,10 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<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">
<HintPath>..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>
@ -116,6 +199,7 @@
<Compile Include="FakeExtensionTypeLocator.cs" />
<Compile Include="FakeNameResolver.cs" />
<Compile Include="InvariantCultureFixture.cs" />
<Compile Include="LogMessage.cs" />
<Compile Include="NullConsoleProvider.cs" />
<Compile Include="NullFunctionInstanceLogger.cs" />
<Compile Include="NullFunctionOutputLoggerProvider.cs" />
@ -123,6 +207,8 @@
<Compile Include="NullHostInstanceLoggerProvider.cs" />
<Compile Include="ScriptHelpers.cs" />
<Compile Include="TestHelpers.cs" />
<Compile Include="TestLogger.cs" />
<Compile Include="TestLoggerProvider.cs" />
<Compile Include="TestTraceWriter.cs" />
<Compile Include="WebJobsShutdownContext.cs" />
<Compile Include="ExceptionAssert.cs" />

Просмотреть файл

@ -1,23 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<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"/>
<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" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<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>
</assemblyBinding>
</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.OData" 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="NETStandard.Library" version="1.6.1" targetFramework="net46" />
<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.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.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.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.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.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="xunit" version="2.1.0" targetFramework="net45" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />

Просмотреть файл

@ -3,12 +3,14 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
@ -44,7 +46,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
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.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
@ -64,7 +66,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
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.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
@ -76,7 +78,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
public void StartFunctionTimeout_NoTimeout_ReturnsNull()
{
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);
}
@ -90,7 +92,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
TimeoutAttribute attribute = typeof(Functions).GetCustomAttribute<TimeoutAttribute>();
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);
@ -109,7 +111,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
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.Equal(attribute.Timeout.TotalMilliseconds, timer.Interval);
@ -317,16 +319,26 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Executors
TimeoutAttribute attribute = method.GetCustomAttribute<TimeoutAttribute>();
Guid instanceId = Guid.Parse("B2D1DD72-80E2-412B-A22E-3B4558F378B4");
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.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];
Assert.Equal(TraceLevel.Error, trace.Level);
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);
// verify ILogger
LogMessage log = logger.LogMessages.Single();
Assert.Equal(LogLevel.Error, log.Level);
Assert.Equal(message, log.FormattedMessage);
}
public static void GlobalLevel(CancellationToken cancellationToken)

Просмотреть файл

@ -5,16 +5,14 @@ using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Blobs;
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.Storage;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Timers;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Moq;
@ -22,7 +20,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
{
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);
@ -42,7 +41,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
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)

Просмотреть файл

@ -11,9 +11,9 @@ using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Azure.WebJobs.Host.Executors;
using Microsoft.Azure.WebJobs.Host.Indexers;
using Microsoft.Azure.WebJobs.Host.Storage.Blob;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
@ -35,6 +35,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
Mock<IFunctionExecutor> executorMock = new Mock<IFunctionExecutor>(MockBehavior.Strict);
IFunctionIndexCollector stubIndex = new Mock<IFunctionIndexCollector>().Object;
FunctionIndexer indexer = new FunctionIndexer(
new Mock<ITriggerBindingProvider>(MockBehavior.Strict).Object,
new Mock<IBindingProvider>(MockBehavior.Strict).Object,
@ -42,7 +43,8 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
executorMock.Object,
extensionsMock.Object,
new SingletonManager(),
new TestTraceWriter(TraceLevel.Verbose));
new TestTraceWriter(TraceLevel.Verbose),
null);
Assert.Throws<FunctionIndexingException>(() => indexer.IndexMethodAsync(method, stubIndex, CancellationToken.None).GetAwaiter().GetResult());
}

Просмотреть файл

@ -11,9 +11,11 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Indexers;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
@ -151,16 +153,28 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
public async Task IndexMethod_IfMethodReturnsAsyncVoid_Throws()
{
var traceWriter = new TestTraceWriter(TraceLevel.Verbose);
var loggerFactory = new LoggerFactory();
var loggerProvider = new TestLoggerProvider();
loggerFactory.AddProvider(loggerProvider);
// Arrange
IFunctionIndexCollector index = CreateStubFunctionIndex();
FunctionIndexer product = CreateProductUnderTest(traceWriter: traceWriter);
FunctionIndexer product = CreateProductUnderTest(traceWriter: traceWriter, loggerFactory: loggerFactory);
// Act & Assert
await product.IndexMethodAsync(typeof(FunctionIndexerTests).GetMethod("ReturnAsyncVoid"), index, CancellationToken.None);
var warning = traceWriter.Traces.First(p => p.Level == TraceLevel.Warning);
Assert.Equal("Function 'ReturnAsyncVoid' is async but does not return a Task. Your function may not run correctly.", warning.Message);
string expectedMessage = "Function 'ReturnAsyncVoid' is async but does not return a Task. Your function may not run correctly.";
// 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]
@ -317,9 +331,9 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Indexers
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()

Просмотреть файл

@ -2,17 +2,17 @@
// 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 System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Indexers;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
using System.Collections.ObjectModel;
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
{
@ -25,6 +25,16 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
ShortName = "testfunc"
};
private readonly ILoggerFactory _loggerFactory;
private readonly TestLoggerProvider _loggerProvider;
public FunctionListenerTests()
{
_loggerFactory = new LoggerFactory();
_loggerProvider = new TestLoggerProvider();
_loggerFactory.AddProvider(_loggerProvider);
}
[Fact]
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);
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
.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 exc = trace.Traces[0].Exception as FunctionException;
Assert.Equal("testfunc", exc.MethodName);
Assert.False(exc.Handled);
// Validate TraceWriter
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();
}
@ -48,14 +66,25 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
.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);
Assert.Equal("The listener for function 'testfunc' was unable to start.", trace.Traces[0].Message);
var exc = trace.Traces[0].Exception as FunctionException;
Assert.Equal("testfunc", exc.MethodName);
Assert.True(exc.Handled);
string expectedMessage = "The listener for function 'testfunc' was unable to start.";
// Validate TraceWriter
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();
}
@ -66,7 +95,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
badListener.Setup(bl => bl.StartAsync(It.IsAny<CancellationToken>()))
.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);
// 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));
goodListener.Setup(bl => bl.StopAsync(It.IsAny<CancellationToken>()))
.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.StopAsync(ct);
Assert.Empty(trace.Traces);
Assert.Empty(_loggerProvider.CreatedLoggers.Single().LogMessages);
goodListener.VerifyAll();
}

Просмотреть файл

@ -4,14 +4,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
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.Protocols;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
@ -43,6 +46,10 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
SingletonManager singletonManager = new SingletonManager();
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
List<FunctionDefinition> functions = new List<FunctionDefinition>();
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
// 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);
string expectedMessage = $"Function '{descriptor.ShortName}' is disabled";
// Validate TraceWriter
Assert.Equal(1, traceWriter.Traces.Count);
Assert.Equal(TraceLevel.Info, traceWriter.Traces[0].Level);
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);
}

Просмотреть файл

@ -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));
}
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше