Skip to content

Commit 678fa48

Browse files
committed
Add a new ETW event to collect analytics about functions
1 parent fc228bf commit 678fa48

16 files changed

+178
-42
lines changed

src/WebJobs.Script.WebHost/Diagnostics/MetricsEventManager.cs

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

44
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
57
using System.Threading;
68
using System.Threading.Tasks;
7-
9+
using Microsoft.Azure.WebJobs.Script;
10+
using Microsoft.Azure.WebJobs.Script.Description;
811
using Microsoft.Diagnostics.Tracing;
912

1013
namespace WebJobs.Script.WebHost.Diagnostics
1114
{
1215
public static class MetricsEventManager
1316
{
1417
private static FunctionActivityTracker instance = null;
15-
1618
private static object functionActivityTrackerLockObject = new object();
1719

1820
public static void FunctionStarted()
@@ -43,6 +45,52 @@ public static void FunctionCompleted()
4345
}
4446
}
4547

48+
public static void HostStarted(ScriptHost scriptHost)
49+
{
50+
if (scriptHost == null || scriptHost.Functions == null)
51+
{
52+
return;
53+
}
54+
55+
var siteName = GetNormalizedString(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"));
56+
foreach (var function in scriptHost.Functions)
57+
{
58+
if (function == null || function.Metadata == null)
59+
{
60+
continue;
61+
}
62+
63+
MetricEventSource.Log.RaiseFunctionsInfoEvent(
64+
siteName,
65+
GetNormalizedString(function.Name),
66+
function.Metadata != null
67+
? SerializeBindings(function.Metadata.InputBindings)
68+
: GetNormalizedString(null),
69+
function.Metadata != null
70+
? SerializeBindings(function.Metadata.OutputBindings)
71+
: GetNormalizedString(null),
72+
function.Metadata.ScriptType.ToString(),
73+
function.Metadata != null ? function.Metadata.IsDisabled : false);
74+
}
75+
}
76+
77+
private static string SerializeBindings(IEnumerable<BindingMetadata> bindings)
78+
{
79+
if (bindings != null)
80+
{
81+
return string.Join(",", bindings.ToList().Select(b => b.Type.ToString()));
82+
}
83+
else
84+
{
85+
return GetNormalizedString(null);
86+
}
87+
}
88+
89+
private static string GetNormalizedString(string input)
90+
{
91+
return input ?? string.Empty;
92+
}
93+
4694
private class FunctionActivityTracker : IDisposable
4795
{
4896
private readonly string executionId = Guid.NewGuid().ToString();
@@ -132,19 +180,28 @@ private static void WriteFunctionsMetricEvent(string executionId, ulong executio
132180
{
133181
MetricEventSource.Log.RaiseFunctionsMetricEvent(executionId, executionTimeSpan, executionCount, executionStage);
134182
}
183+
}
135184

136-
[EventSource(Guid = "08D0D743-5C24-43F9-9723-98277CEA5F9B")]
137-
private sealed class MetricEventSource : EventSource
185+
[EventSource(Guid = "08D0D743-5C24-43F9-9723-98277CEA5F9B")]
186+
private sealed class MetricEventSource : EventSource
187+
{
188+
internal static readonly MetricEventSource Log = new MetricEventSource();
189+
190+
[Event(57906, Level = EventLevel.Informational, Channel = EventChannel.Operational)]
191+
public void RaiseFunctionsMetricEvent(string executionId, ulong executionTimeSpan, ulong executionCount, string executionStage)
138192
{
139-
internal static readonly MetricEventSource Log = new MetricEventSource();
193+
if (IsEnabled())
194+
{
195+
WriteEvent(57906, executionId, executionTimeSpan, executionCount, executionStage);
196+
}
197+
}
140198

141-
[Event(57906, Level = EventLevel.Informational, Channel = EventChannel.Operational)]
142-
public void RaiseFunctionsMetricEvent(string executionId, ulong executionTimeSpan, ulong executionCount, string executionStage)
199+
[Event(57908, Level = EventLevel.Informational, Channel = EventChannel.Operational)]
200+
public void RaiseFunctionsInfoEvent(string siteName, string functionName, string inputBindings, string outputBindings, string scriptType, bool isDisabled)
201+
{
202+
if (IsEnabled())
143203
{
144-
if (IsEnabled())
145-
{
146-
WriteEvent(57906, executionId, executionTimeSpan, executionCount, executionStage);
147-
}
204+
WriteEvent(57908, siteName, functionName, inputBindings, outputBindings, scriptType, isDisabled);
148205
}
149206
}
150207
}

src/WebJobs.Script.WebHost/Diagnostics/WebHostMetricsLogger.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,14 @@ public void EndEvent(MetricEvent metricEvent)
2727
MetricsEventManager.FunctionCompleted();
2828
}
2929
}
30+
31+
public void LogEvent(MetricEvent metricEvent)
32+
{
33+
HostStarted hostStartedEvent = metricEvent as HostStarted;
34+
if (hostStartedEvent != null)
35+
{
36+
MetricsEventManager.HostStarted(hostStartedEvent.Host);
37+
}
38+
}
3039
}
3140
}

src/WebJobs.Script.WebHost/WebScriptHostManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ namespace WebJobs.Script.WebHost
1818
{
1919
public class WebScriptHostManager : ScriptHostManager
2020
{
21+
private IMetricsLogger _metricsLogger;
22+
2123
public WebScriptHostManager(ScriptHostConfiguration config) : base(config)
2224
{
25+
_metricsLogger = new WebHostMetricsLogger();
2326
}
2427

2528
private IDictionary<string, FunctionDescriptor> HttpFunctions { get; set; }
@@ -86,9 +89,9 @@ public FunctionDescriptor GetHttpFunctionOrNull(Uri uri)
8689
protected override void OnInitializeConfig(JobHostConfiguration config)
8790
{
8891
base.OnInitializeConfig(config);
89-
92+
9093
// Add our WebHost specific services
91-
config.AddService<IMetricsLogger>(new WebHostMetricsLogger());
94+
config.AddService<IMetricsLogger>(_metricsLogger);
9295
}
9396

9497
protected override void OnHostStarted()

src/WebJobs.Script/Description/CSharp/CSharpFunctionDescriptionProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Collections.Generic;
66
using System.Collections.ObjectModel;
77
using System.Globalization;
8-
using System.IO;
98
using System.Linq;
109
using System.Reflection;
1110
using System.Reflection.Emit;
@@ -46,8 +45,7 @@ public override bool TryCreate(FunctionMetadata functionMetadata, out FunctionDe
4645

4746
functionDescriptor = null;
4847

49-
string extension = Path.GetExtension(functionMetadata.Source).ToLower(CultureInfo.InvariantCulture);
50-
if (string.Compare(extension, ".csx", StringComparison.OrdinalIgnoreCase) != 0)
48+
if (functionMetadata.ScriptType != ScriptType.CSharp)
5149
{
5250
return false;
5351
}

src/WebJobs.Script/Description/FunctionMetadata.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public FunctionMetadata()
1818

1919
public string Source { get; set; }
2020

21+
public ScriptType ScriptType { get; set; }
22+
2123
public bool IsDisabled { get; set; }
2224

2325
public Collection<BindingMetadata> Bindings { get; private set; }

src/WebJobs.Script/Description/NodeFunctionDescriptorProvider.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
using System;
55
using System.Collections.ObjectModel;
6-
using System.Globalization;
7-
using System.IO;
86
using Microsoft.Azure.WebJobs.Script.Binding;
97

108
namespace Microsoft.Azure.WebJobs.Script.Description
@@ -25,8 +23,7 @@ public override bool TryCreate(FunctionMetadata functionMetadata, out FunctionDe
2523

2624
functionDescriptor = null;
2725

28-
string extension = Path.GetExtension(functionMetadata.Source).ToLower(CultureInfo.InvariantCulture);
29-
if (!(extension == ".js" || string.IsNullOrEmpty(extension)))
26+
if (functionMetadata.ScriptType != ScriptType.Javascript)
3027
{
3128
return false;
3229
}

src/WebJobs.Script/Description/ScriptFunctionDescriptionProvider.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public override bool TryCreate(FunctionMetadata functionMetadata, out FunctionDe
2525

2626
functionDescriptor = null;
2727

28-
string extension = Path.GetExtension(functionMetadata.Source).ToLower(CultureInfo.InvariantCulture);
29-
if (!ScriptFunctionInvoker.IsSupportedScriptType(extension))
28+
if (!ScriptFunctionInvoker.IsSupportedScriptType(functionMetadata.ScriptType))
3029
{
3130
return false;
3231
}

src/WebJobs.Script/Description/ScriptFunctionInvoker.cs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ public class ScriptFunctionInvoker : ScriptFunctionInvokerBase
2222
{
2323
private const string BashPathEnvironmentKey = "AzureWebJobs_BashPath";
2424
private const string ProgramFiles64bitKey = "ProgramW6432";
25-
private static string[] _supportedScriptTypes = new string[] { "ps1", "cmd", "bat", "py", "php", "sh", "fsx" };
25+
private static ScriptType[] _supportedScriptTypes = new ScriptType[] { ScriptType.Powershell, ScriptType.WindowsBatch, ScriptType.Python, ScriptType.PHP, ScriptType.Bash, ScriptType.FSharp };
2626
private readonly string _scriptFilePath;
27-
private readonly string _scriptType;
2827
private readonly IMetricsLogger _metrics;
2928

3029
private readonly Collection<FunctionBinding> _inputBindings;
@@ -34,51 +33,43 @@ internal ScriptFunctionInvoker(string scriptFilePath, ScriptHost host, FunctionM
3433
: base(host, functionMetadata)
3534
{
3635
_scriptFilePath = scriptFilePath;
37-
_scriptType = Path.GetExtension(_scriptFilePath).ToLower(CultureInfo.InvariantCulture).TrimStart('.');
3836
_inputBindings = inputBindings;
3937
_outputBindings = outputBindings;
4038
_metrics = host.ScriptConfig.HostConfig.GetService<IMetricsLogger>();
4139
}
4240

43-
public static bool IsSupportedScriptType(string extension)
41+
public static bool IsSupportedScriptType(ScriptType scriptType)
4442
{
45-
if (string.IsNullOrEmpty(extension))
46-
{
47-
throw new ArgumentNullException("extension");
48-
}
49-
50-
string scriptType = extension.ToLower(CultureInfo.InvariantCulture).TrimStart('.');
5143
return _supportedScriptTypes.Contains(scriptType);
5244
}
5345

5446
public override async Task Invoke(object[] parameters)
5547
{
5648
string scriptHostArguments;
57-
switch (_scriptType)
49+
switch (Metadata.ScriptType)
5850
{
59-
case "ps1":
51+
case ScriptType.Powershell:
6052
scriptHostArguments = string.Format("-ExecutionPolicy RemoteSigned -File \"{0}\"", _scriptFilePath);
6153
await ExecuteScriptAsync("PowerShell.exe", scriptHostArguments, parameters);
6254
break;
63-
case "cmd":
64-
case "bat":
55+
case ScriptType.WindowsBatch:
6556
scriptHostArguments = string.Format("/c \"{0}\"", _scriptFilePath);
6657
await ExecuteScriptAsync("cmd", scriptHostArguments, parameters);
6758
break;
68-
case "py":
59+
case ScriptType.Python:
6960
scriptHostArguments = string.Format("\"{0}\"", _scriptFilePath);
7061
await ExecuteScriptAsync("python.exe", scriptHostArguments, parameters);
7162
break;
72-
case "php":
63+
case ScriptType.PHP:
7364
scriptHostArguments = string.Format("\"{0}\"", _scriptFilePath);
7465
await ExecuteScriptAsync("php.exe", scriptHostArguments, parameters);
7566
break;
76-
case "sh":
67+
case ScriptType.Bash:
7768
scriptHostArguments = string.Format("\"{0}\"", _scriptFilePath);
7869
string bashPath = ResolveBashPath();
7970
await ExecuteScriptAsync(bashPath, scriptHostArguments, parameters);
8071
break;
81-
case "fsx":
72+
case ScriptType.FSharp:
8273
scriptHostArguments = string.Format("/c fsi.exe \"{0}\"", _scriptFilePath);
8374
await ExecuteScriptAsync("cmd", scriptHostArguments, parameters);
8475
break;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.Description
5+
{
6+
public enum ScriptType
7+
{
8+
Javascript,
9+
CSharp,
10+
Powershell,
11+
WindowsBatch,
12+
Python,
13+
PHP,
14+
Bash,
15+
FSharp,
16+
Unknown
17+
}
18+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
5+
{
6+
public class HostStarted : MetricEvent
7+
{
8+
public HostStarted(ScriptHost host)
9+
{
10+
Host = host;
11+
}
12+
13+
public ScriptHost Host { get; private set; }
14+
}
15+
}

0 commit comments

Comments
 (0)