Skip to content

Commit 3b9449e

Browse files
Added filter to show only contextual logs from App Insights. (#8278)
* Added filter to show only contextual logs from App Insights. * Update src/WebJobs.Script.WebHost/Controllers/FunctionsController.cs Co-authored-by: Lilian Kasem <[email protected]> * Update src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs Co-authored-by: Lilian Kasem <[email protected]> * Added try/finally to stop the activity * Update release_notes.md * Try/Finally Co-authored-by: Lilian Kasem <[email protected]>
1 parent c9debc4 commit 3b9449e

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

release_notes.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
- My change description (#PR)
44
-->
55

6+
67
**Release sprint:** Sprint 118
7-
[ [bugs](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Abug+is%3Aclosed) | [features](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Afeature+is%3Aclosed) ]
8+
[ [bugs](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Abug+is%3Aclosed) | [features](https://github.com/Azure/azure-functions-host/issues?q=is%3Aissue+milestone%3A%22Functions+Sprint+118%22+label%3Afeature+is%3Aclosed) ]
9+

src/WebJobs.Script.WebHost/Controllers/FunctionsController.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.IO.Abstractions;
67
using System.IO.Compression;
78
using System.Linq;
@@ -118,6 +119,8 @@ public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocatio
118119
{
119120
{ inputParameter.Name, invocation.Input }
120121
};
122+
// LiveLogs session id is used to show only contextual logs in the "Code + Test" experience.
123+
string sessionId = this.Request?.Headers[ScriptConstants.LiveLogsSessionAIKey];
121124

122125
using (System.Threading.ExecutionContext.SuppressFlow())
123126
{
@@ -130,7 +133,25 @@ public IActionResult Invoke(string name, [FromBody] FunctionInvocation invocatio
130133

131134
using (_logger.BeginScope(loggerScope))
132135
{
133-
await scriptHost.CallAsync(function.Name, arguments);
136+
if (!string.IsNullOrWhiteSpace(sessionId))
137+
{
138+
// Current activity is null due to SuppressFlow. Start a new activity and set baggage so that it's included in the custom dimensions.
139+
Activity activity = new Activity($"{nameof(FunctionsController)}.Invoke");
140+
activity?.Start();
141+
activity?.AddBaggage(ScriptConstants.LiveLogsSessionAIKey, sessionId);
142+
try
143+
{
144+
await scriptHost.CallAsync(function.Name, arguments);
145+
}
146+
finally
147+
{
148+
activity?.Stop();
149+
}
150+
}
151+
else
152+
{
153+
await scriptHost.CallAsync(function.Name, arguments);
154+
}
134155
}
135156
});
136157
}

src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Linq;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -42,6 +43,12 @@ public async Task Invoke(HttpContext context)
4243
var functionExecution = context.Features.Get<IFunctionExecutionFeature>();
4344
if (functionExecution != null && !context.Response.HasStarted)
4445
{
46+
// LiveLogs session id is used to show only contextual logs in the "Code + Test" experience. The id is included in the custom dimension.
47+
string sessionId = context.Request?.Headers[ScriptConstants.LiveLogsSessionAIKey];
48+
if (!string.IsNullOrWhiteSpace(sessionId))
49+
{
50+
Activity.Current?.AddBaggage(ScriptConstants.LiveLogsSessionAIKey, sessionId);
51+
}
4552
IActionResult result = await GetResultAsync(context, functionExecution);
4653
ActionContext actionContext = new ActionContext(context, context.GetRouteData(), new ActionDescriptor());
4754
await result.ExecuteResultAsync(actionContext);

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,5 +191,6 @@ public static class ScriptConstants
191191
public static readonly ImmutableArray<string> SystemLogCategoryPrefixes = ImmutableArray.Create("Microsoft.Azure.WebJobs.", "Function.", "Worker.", "Host.");
192192

193193
public static readonly string FunctionMetadataDirectTypeKey = "DirectType";
194+
public static readonly string LiveLogsSessionAIKey = "#AzFuncLiveLogsSessionId";
194195
}
195196
}

test/WebJobs.Script.Tests/Controllers/Admin/FunctionsControllerTests.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Collections.ObjectModel;
7+
using System.Diagnostics;
78
using System.IO;
89
using System.Net;
910
using System.Threading;
@@ -94,6 +95,65 @@ public async Task Invoke_CallsFunction()
9495
Assert.True(functionInvoked);
9596
}
9697

98+
[Fact]
99+
public async Task Invoke_CallsFunctionSession()
100+
{
101+
var testFunctions = new Collection<FunctionDescriptor>();
102+
string testFunctionName = "TestFunction";
103+
string triggerParameterName = "testTrigger";
104+
string testInput = Guid.NewGuid().ToString();
105+
string sessionId = Guid.NewGuid().ToString();
106+
bool baggageAdded = false;
107+
108+
var scriptHostMock = new Mock<IScriptJobHost>();
109+
scriptHostMock.Setup(p => p.CallAsync(It.IsAny<string>(), It.IsAny<IDictionary<string, object>>(), CancellationToken.None))
110+
.Callback<string, IDictionary<string, object>, CancellationToken>((name, args, token) =>
111+
{
112+
if (string.Equals(Activity.Current?.GetBaggageItem(ScriptConstants.LiveLogsSessionAIKey), sessionId, StringComparison.OrdinalIgnoreCase))
113+
{
114+
baggageAdded = true;
115+
}
116+
})
117+
.Returns(Task.CompletedTask);
118+
119+
scriptHostMock.Setup(p => p.Functions).Returns(testFunctions);
120+
Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>
121+
{
122+
new ParameterDescriptor("context", typeof(ExecutionContext)),
123+
new ParameterDescriptor("log", typeof(TraceWriter)),
124+
new ParameterDescriptor(triggerParameterName, typeof(string))
125+
{
126+
IsTrigger = true
127+
}
128+
};
129+
testFunctions.Add(new FunctionDescriptor(testFunctionName, null, null, parameters, null, null, null));
130+
131+
FunctionInvocation invocation = new FunctionInvocation
132+
{
133+
Input = testInput
134+
};
135+
136+
var scriptPath = Path.GetTempPath();
137+
var applicationHostOptions = new ScriptApplicationHostOptions();
138+
applicationHostOptions.ScriptPath = scriptPath;
139+
var optionsWrapper = new OptionsWrapper<ScriptApplicationHostOptions>(applicationHostOptions);
140+
var functionsManagerMock = new Mock<IWebFunctionsManager>();
141+
var mockRouter = new Mock<IWebJobsRouter>();
142+
var testController = new FunctionsController(functionsManagerMock.Object, mockRouter.Object, new LoggerFactory(), optionsWrapper);
143+
var httpContext = new DefaultHttpContext();
144+
httpContext.Request.Headers.Add(ScriptConstants.LiveLogsSessionAIKey, sessionId);
145+
testController.ControllerContext.HttpContext = httpContext;
146+
147+
//Test Code
148+
IActionResult response = testController.Invoke(testFunctionName, invocation, scriptHostMock.Object);
149+
Assert.IsType<AcceptedResult>(response);
150+
151+
// The call is fire-and-forget, so watch for functionInvoked to be set.
152+
await TestHelpers.Await(() => baggageAdded, timeout: 3000, pollingInterval: 100);
153+
154+
Assert.True(baggageAdded);
155+
}
156+
97157
private static FunctionsController SetUpFunctionsController(string testFunctionName, bool isFileSystemReadOnly, bool functionCreatedSuccess = true)
98158
{
99159
var scriptPath = Path.GetTempPath();

0 commit comments

Comments
 (0)