Skip to content

Commit cb0122e

Browse files
committed
Add new Azure Functions sample for disabled logging
Ran into an issue where re-running the same Azure Function application in our tests would cause the func.exe to fail to acquire a lock for 60s and cause the tests to take longer. This was the simplest workaround.
1 parent f44bb16 commit cb0122e

File tree

10 files changed

+324
-0
lines changed

10 files changed

+324
-0
lines changed

Datadog.Trace.Samples.g.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.AWS.S3", "tracer\te
439439
EndProject
440440
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.GoogleProtobuf", "tracer\test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj", "{EF8C4CCE-E79C-4D78-BF31-222A11E198B9}"
441441
EndProject
442+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.AzureFunctions.V4Isolated.HostLogsDisabled", "tracer\test\test-applications\azure-functions\Samples.AzureFunctions.V4Isolated.HostLogsDisabled\Samples.AzureFunctions.V4Isolated.HostLogsDisabled.csproj", "{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}"
443+
EndProject
442444
Global
443445
GlobalSection(SolutionConfigurationPlatforms) = preSolution
444446
Debug|Any CPU = Debug|Any CPU
@@ -1057,6 +1059,10 @@ Global
10571059
{EF8C4CCE-E79C-4D78-BF31-222A11E198B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
10581060
{EF8C4CCE-E79C-4D78-BF31-222A11E198B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
10591061
{EF8C4CCE-E79C-4D78-BF31-222A11E198B9}.Release|Any CPU.Build.0 = Release|Any CPU
1062+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1063+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Debug|Any CPU.Build.0 = Debug|Any CPU
1064+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Release|Any CPU.ActiveCfg = Release|Any CPU
1065+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Release|Any CPU.Build.0 = Release|Any CPU
10601066
EndGlobalSection
10611067
GlobalSection(NestedProjects) = preSolution
10621068
{9518425A-36A5-4B8F-B0B8-6137DB88441D} = {8CEC2042-F11C-49F5-A674-2355793B600A}
@@ -1232,5 +1238,6 @@ Global
12321238
{E2EDDD17-B5E6-4240-9EF8-34F2D274AA19} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
12331239
{0C0578CB-3B67-4F95-8547-206CD2A560CD} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
12341240
{EF8C4CCE-E79C-4D78-BF31-222A11E198B9} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
1241+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C} = {C4C1E313-C7C1-4490-AECE-0DD0062380A4}
12351242
EndGlobalSection
12361243
EndGlobal

Datadog.Trace.sln

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks.OpenTelemetry.In
617617
EndProject
618618
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Datadog.AzureFunctions", "tracer\src\Datadog.AzureFunctions\Datadog.AzureFunctions.csproj", "{BB073E40-F46D-E52B-662E-395EED834111}"
619619
EndProject
620+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.AzureFunctions.V4Isolated.HostLogsDisabled", "tracer\test\test-applications\azure-functions\Samples.AzureFunctions.V4Isolated.HostLogsDisabled\Samples.AzureFunctions.V4Isolated.HostLogsDisabled.csproj", "{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}"
621+
EndProject
620622
Global
621623
GlobalSection(SolutionConfigurationPlatforms) = preSolution
622624
Debug|Any CPU = Debug|Any CPU
@@ -1489,6 +1491,10 @@ Global
14891491
{BB073E40-F46D-E52B-662E-395EED834111}.Debug|Any CPU.Build.0 = Debug|Any CPU
14901492
{BB073E40-F46D-E52B-662E-395EED834111}.Release|Any CPU.ActiveCfg = Release|Any CPU
14911493
{BB073E40-F46D-E52B-662E-395EED834111}.Release|Any CPU.Build.0 = Release|Any CPU
1494+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1495+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Debug|Any CPU.Build.0 = Debug|Any CPU
1496+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Release|Any CPU.ActiveCfg = Release|Any CPU
1497+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C}.Release|Any CPU.Build.0 = Release|Any CPU
14921498
EndGlobalSection
14931499
GlobalSection(SolutionProperties) = preSolution
14941500
HideSolutionNode = FALSE
@@ -1731,6 +1737,7 @@ Global
17311737
{501AD343-1951-FF43-0C7F-3FE3FDF5A660} = {E5439139-6F94-44FA-9590-C32FCC1C7A93}
17321738
{BD7ADF79-C948-5CC7-6BE8-A7FF8DF882C3} = {E5439139-6F94-44FA-9590-C32FCC1C7A93}
17331739
{BB073E40-F46D-E52B-662E-395EED834111} = {9E5F0022-0A50-40BF-AC6A-C3078585ECAB}
1740+
{C770F9F8-0430-587D-EB7A-8BEC2FE9B61C} = {C4C1E313-C7C1-4490-AECE-0DD0062380A4}
17341741
EndGlobalSection
17351742
GlobalSection(ExtensibilityGlobals) = postSolution
17361743
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
@@ -1857,6 +1864,7 @@ Global
18571864
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c4abf344-3263-45d5-a074-03fb206ff309}*SharedItemsImports = 5
18581865
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c4cdf6a6-40e5-4ccd-ac4c-143f9f4398ca}*SharedItemsImports = 5
18591866
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c5e7978a-de2a-4944-86db-4721a110e720}*SharedItemsImports = 5
1867+
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c770f9f8-0430-587d-eb7a-8bec2fe9b61c}*SharedItemsImports = 5
18601868
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c7de0626-9eb6-475e-aa0c-cb9de21d4fae}*SharedItemsImports = 5
18611869
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c861221d-9c3b-441f-bbd5-343f49b9dd10}*SharedItemsImports = 4
18621870
tracer\test\test-applications\Samples.Shared\Samples.Shared.projitems*{c94e0739-1da0-4657-8d53-fa4143f6fda3}*SharedItemsImports = 5
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Net;
5+
using System.Net.Http.Headers;
6+
using Microsoft.Azure.Functions.Worker;
7+
using Microsoft.Azure.Functions.Worker.Http;
8+
using Microsoft.Extensions.Hosting;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Samples.AzureFunctions.AllTriggers;
12+
13+
public class AllTriggers
14+
{
15+
private readonly IHostApplicationLifetime _lifetime;
16+
private const string AtMidnightOnFirstJan = "0 0 0 1 Jan *";
17+
private static readonly HttpClient HttpClient = new();
18+
private static readonly ManualResetEventSlim _mutex = new(initialState: false, spinCount: 0);
19+
20+
private readonly ILogger _logger;
21+
22+
public AllTriggers(ILogger<AllTriggers> logger, IHostApplicationLifetime lifetime)
23+
{
24+
_lifetime = lifetime;
25+
_logger = logger;
26+
}
27+
28+
[Function("TriggerAllTimer")]
29+
public async Task TriggerAllTimer([TimerTrigger(AtMidnightOnFirstJan, RunOnStartup = true)] TimerInfo myTimer)
30+
{
31+
_logger.LogInformation($"Profiler attached: {SampleHelpers.IsProfilerAttached()}");
32+
_logger.LogInformation($"Profiler assembly location: {SampleHelpers.GetTracerAssemblyLocation()}");
33+
34+
var envVars = string.Join(", ", SampleHelpers.GetDatadogEnvironmentVariables().Select(x => $"{x.Key}={x.Value}"));
35+
_logger.LogInformation("$Profiler env vars: {EnvVars}", envVars);
36+
37+
_logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
38+
await CallFunctionHttp("trigger");
39+
_logger.LogInformation($"Trigger All Timer complete: {DateTime.Now}");
40+
_mutex.Set();
41+
}
42+
43+
[Function("ExitApp")]
44+
public void ExitApp([TimerTrigger(AtMidnightOnFirstJan, RunOnStartup = true)] TimerInfo myTimer)
45+
{
46+
_logger.LogInformation($"Waiting for mutex");
47+
if (!_mutex.Wait(TimeSpan.FromMinutes(5)))
48+
{
49+
_logger.LogError($"Error waiting for mutex: not obtained after 5 minutes!");
50+
}
51+
52+
_logger.LogInformation($"Pausing for 3s");
53+
Thread.Sleep(3_000); // just need time for the TriggerAllTimer to finish up etc
54+
55+
_logger.LogInformation($"Calling StopApplication()");
56+
_lifetime.StopApplication();
57+
// brutally kill the host, as can't find any other way to signal it should stop
58+
foreach (var process in Process.GetProcessesByName("func"))
59+
{
60+
_logger.LogInformation("Killing {PID} ({Name})", process.Id, process.ProcessName);
61+
process.Kill();
62+
}
63+
}
64+
65+
[Function("SimpleHttpTrigger")]
66+
public async Task<HttpResponseData> SimpleHttpTrigger(
67+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "simple")] HttpRequestData req)
68+
{
69+
await Task.Yield();
70+
using var s = SampleHelpers.CreateScope("Manual inside Simple");
71+
_logger.LogInformation("C# HTTP trigger function processed a request.");
72+
73+
var res = req.CreateResponse(HttpStatusCode.OK);
74+
res.Headers.Add("Content-Type", "text/plain; charset=utf-8");
75+
await res.WriteStringAsync("This HTTP triggered function executed successfully!");
76+
77+
return res;
78+
}
79+
80+
[Function("TriggerCaller")]
81+
public async Task<HttpResponseData> Trigger(
82+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "trigger")] HttpRequestData req)
83+
{
84+
using var s = SampleHelpers.CreateScope("Manual inside Trigger");
85+
86+
_logger.LogInformation("Calling simple");
87+
await Attempt("simple");
88+
await Attempt("exception", expectFailure: true);
89+
await Attempt("error", expectFailure: true);
90+
await Attempt("badrequest", expectFailure: true);
91+
92+
var res = req.CreateResponse(HttpStatusCode.OK);
93+
res.Headers.Add("Content-Type", "text/plain; charset=utf-8");
94+
await res.WriteStringAsync("Attempting triggers");
95+
96+
return res;
97+
}
98+
99+
[Function("Exception")]
100+
public async Task<HttpResponseData> Exception(
101+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "exception")] HttpRequestData req)
102+
{
103+
using var s = SampleHelpers.CreateScope("Manual inside Exception");
104+
105+
await Task.Yield();
106+
107+
_logger.LogInformation("Called exception HTTP trigger function.");
108+
109+
throw new InvalidOperationException("Task failed successfully.");
110+
}
111+
112+
[Function("ServerError")]
113+
public async Task<HttpResponseData> ServerError(
114+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "error")] HttpRequestData req)
115+
{
116+
using var s = SampleHelpers.CreateScope("Manual inside ServerError");
117+
118+
await Task.Yield();
119+
120+
_logger.LogInformation("Called error HTTP trigger function.");
121+
122+
return req.CreateResponse(HttpStatusCode.InternalServerError);
123+
}
124+
125+
[Function("BadRequest")]
126+
public HttpResponseData BadRequest(
127+
[HttpTrigger(AuthorizationLevel.System, "get", "post", Route = "badrequest")] HttpRequestData req)
128+
{
129+
using var s = SampleHelpers.CreateScope("Manual inside BadRequest");
130+
131+
_logger.LogInformation("Called badrequest HTTP trigger function.");
132+
133+
return req.CreateResponse(HttpStatusCode.BadRequest);
134+
}
135+
136+
private async Task<string> CallFunctionHttp(string path)
137+
{
138+
var httpFunctionUrl = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME") ?? "localhost:7071";
139+
var uri = $"{$"http://{httpFunctionUrl}"}/api/{path}";
140+
_logger.LogInformation("Calling Uri {Uri}", uri);
141+
var simpleResponse = await HttpClient.GetStringAsync(uri);
142+
return simpleResponse;
143+
}
144+
145+
private async Task Attempt(string endpoint, bool expectFailure = false)
146+
{
147+
try
148+
{
149+
await CallFunctionHttp(endpoint);
150+
}
151+
catch (Exception ex)
152+
{
153+
if (expectFailure)
154+
{
155+
_logger.LogInformation(ex, "Trigger attempt failure for {endpoint} as expected", endpoint);
156+
}
157+
else
158+
{
159+
_logger.LogError(ex, "Trigger attempt failure for {endpoint}", endpoint);
160+
}
161+
}
162+
}
163+
164+
public class TimerInfo
165+
{
166+
public MyScheduleStatus ScheduleStatus { get; set; }
167+
168+
public bool IsPastDue { get; set; }
169+
}
170+
171+
public class MyScheduleStatus
172+
{
173+
public DateTime Last { get; set; }
174+
175+
public DateTime Next { get; set; }
176+
177+
public DateTime LastUpdated { get; set; }
178+
}
179+
180+
public class SomeClass
181+
{
182+
public string Name { get; }
183+
184+
public SomeClass(string name)
185+
{
186+
Name = name;
187+
}
188+
}
189+
}
190+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Microsoft.Extensions.Hosting;
2+
using Microsoft.Extensions.Logging;
3+
4+
var host = new HostBuilder()
5+
.ConfigureFunctionsWorkerDefaults()
6+
.ConfigureLogging(logging =>
7+
{
8+
logging.AddConsole();
9+
logging.SetMinimumLevel(LogLevel.Trace);
10+
logging.AddFilter("*", LogLevel.Trace);
11+
})
12+
13+
.Build();
14+
host.Run();
15+
16+
// This application is used to test the Azure Functions V4 Isolated worker with host logs disabled.
17+
// It is the same as V4Isolated, but with the host logs disabled.
18+
// When attempting to run the same Azure Functions project multiple times in our tests
19+
// we were hitting a func.exe Singleton lock issue causing a deadlock of 60s.
20+
// Making a new application seemed to be the simplest solution to avoid the issue.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"profiles": {
3+
"Samples.AzureFunctions.AllTriggers": {
4+
"commandName": "Project",
5+
"environmentVariables": {
6+
"CORECLR_ENABLE_PROFILING": "1",
7+
"CORECLR_PROFILER": "{846F5F1C-F9AE-4B07-969E-05C26BC060D8}",
8+
"CORECLR_PROFILER_PATH": "$(SolutionDir)shared\\bin\\monitoring-home\\tracer\\win-$(Platform)\\Datadog.Trace.ClrProfiler.Native.dll",
9+
10+
"DD_DOTNET_TRACER_HOME": "$(SolutionDir)shared\\bin\\monitoring-home\\tracer",
11+
"DD_VERSION": "1.0.0",
12+
13+
// local.settings.json only applies to the function script, not the host, so this needs to exist here
14+
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
15+
16+
"DD_AZURE_APP_SERVICES": "1",
17+
"DD_TRACE_AZURE_FUNCTIONS_ENABLED": "1",
18+
"DD_LOGS_INJECTION": "1"
19+
},
20+
"nativeDebugging": true
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"dependencies": {
3+
"appInsights1": {
4+
"type": "appInsights"
5+
},
6+
"storage1": {
7+
"type": "storage",
8+
"connectionId": "AzureWebJobsStorage"
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"dependencies": {
3+
"appInsights1": {
4+
"type": "appInsights.sdk"
5+
},
6+
"storage1": {
7+
"type": "storage.emulator",
8+
"connectionId": "AzureWebJobsStorage"
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
4+
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
5+
<OutputType>Exe</OutputType>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
10+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.3.1" />
11+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.4" />
12+
<!-- We can't build and run these as multi-package versions, so we have to manually update this as required for now -->
13+
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<None Update="host.json">
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
</None>
20+
<None Update="local.settings.json">
21+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
22+
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
23+
</None>
24+
</ItemGroup>
25+
<ItemGroup>
26+
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
27+
</ItemGroup>
28+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"logLevel": {
5+
"default": "Trace",
6+
"Host.*": "Trace",
7+
"Microsoft.*": "Trace"
8+
},
9+
"console": {
10+
"isEnabled": true
11+
},
12+
"applicationInsights": {
13+
"samplingSettings": {
14+
"isEnabled": true,
15+
"excludedTypes": "Request"
16+
}
17+
}
18+
}
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
5+
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
6+
}
7+
}

0 commit comments

Comments
 (0)