Skip to content

Commit f9edc02

Browse files
authored
Merge pull request #21560 from unoplatform/dev/cdb/analytics/app-lifecycle
App Launch monitoring
2 parents 06eba26 + b626a6d commit f9edc02

File tree

54 files changed

+4730
-498
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4730
-498
lines changed

src/Directory.Build.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<PackageReference Update="AwesomeAssertions" Version="9.2.0" />
134134
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="5.0.0" />
135135
<PackageReference Update="Microsoft.Extensions.Logging.Debug" Version="5.0.0" />
136+
<PackageReference Update="Microsoft.Extensions.TimeProvider.Testing" Version="9.9.0" />
136137
<PackageReference Update="Uno.Extensions.Logging.WebAssembly.Console" Version="1.7.0" />
137138
<PackageReference Update="Uno.Extensions.Logging.WebAssembly.OSLog" Version="1.4.0" />
138139
<PackageReference Update="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Http;
5+
using Microsoft.Build.Framework;
6+
using Microsoft.Build.Utilities;
7+
8+
namespace Uno.Sdk.Tasks;
9+
10+
public sealed class UnoNotifyAppLaunchToDevServer_v0 : Task
11+
{
12+
[Required] public string Port { get; set; } = string.Empty;
13+
[Required] public string TargetPath { get; set; } = string.Empty;
14+
public string IsDebug { get; set; } = string.Empty;
15+
public string Ide { get; set; } = string.Empty;
16+
public string Plugin { get; set; } = string.Empty;
17+
18+
[Output] public bool Success { get; set; }
19+
[Output] public string ResponseContent { get; set; } = string.Empty;
20+
21+
public override bool Execute()
22+
{
23+
// Validate
24+
if (string.IsNullOrWhiteSpace(Port) || !ushort.TryParse(Port, out var portNum) || portNum == 0)
25+
{
26+
Log.LogError("UnoRemoteControlPort must be a valid port number between 1 and 65535.");
27+
return Success = false;
28+
}
29+
if (string.IsNullOrWhiteSpace(TargetPath))
30+
{
31+
Log.LogError("TargetPath is required.");
32+
return Success = false;
33+
}
34+
35+
var encodedPath = WebUtility.UrlEncode(TargetPath);
36+
37+
var parts = new List<string>();
38+
void Add(string name, string value)
39+
{
40+
if (!string.IsNullOrEmpty(value))
41+
parts.Add($"{name}={Uri.EscapeDataString(value)}");
42+
}
43+
44+
Add("ide", Ide);
45+
Add("plugin", Plugin);
46+
47+
// IsDebug is optional: treat empty/null as "false"
48+
var isDebugValue = false;
49+
if (!string.IsNullOrEmpty(IsDebug) && bool.TryParse(IsDebug, out var parsed))
50+
{
51+
isDebugValue = parsed;
52+
}
53+
Add("isDebug", isDebugValue ? "true" : "false");
54+
55+
var qs = parts.Count > 0 ? "?" + string.Join("&", parts) : string.Empty;
56+
var url = $"http://localhost:{portNum}/applaunch/asm/{encodedPath}{qs}";
57+
58+
try
59+
{
60+
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
61+
var response = client.GetAsync(url).GetAwaiter().GetResult();
62+
63+
Log.LogMessage(MessageImportance.High,
64+
$"[NotifyDevServer] GET {url} -> {(int)response.StatusCode} {response.ReasonPhrase}");
65+
66+
if (response.IsSuccessStatusCode)
67+
{
68+
ResponseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
69+
Success = true;
70+
return true;
71+
}
72+
else
73+
{
74+
Success = false;
75+
ResponseContent = string.Empty;
76+
return false;
77+
}
78+
}
79+
catch (Exception ex)
80+
{
81+
Log.LogWarning($"[NotifyDevServer] GET {url} failed: {ex.GetType().Name}: {ex.Message}");
82+
Success = false;
83+
ResponseContent = string.Empty;
84+
return false;
85+
}
86+
}
87+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
4+
<!--
5+
This file defines the UnoNotifyAppLaunch target used to notify the DevServer (RemoteControl)
6+
when an application has been launched, so that the DevServer can monitor that the launched app
7+
connects back in a timely manner. This is used to detect launch failures and report them to telemetry.
8+
9+
This target is intended to be invoked after the application has been built, but before it is launched, by
10+
the IDE tooling (e.g. Visual Studio Code, Rider, etc). Is it not required for VisualStudio, which is using
11+
a different mechanism to do the same thing. The implementation in this version is in the `AppLaunch` folder
12+
of the `Uno.UI.RemoteControl.VS` project.
13+
14+
IMPORTANT: This target should only be called with Debug configuration (-c:Debug or -p:Configuration=Debug).
15+
It will fail with an error if called with any other configuration (e.g., Release).
16+
17+
Other IDEs will call this target by specifying the `UnoRemoteControlPort` property using the following command:
18+
19+
```shell
20+
dotnet build <app csproj> -c:Debug -t:UnoNotifyAppLaunch -p:TargetFramework=<tfm to launch> -p:NoBuild=true -restore:false -v:d -noLogo -low -p:UnoIde=<ide info> -p:UnoPlugin=<Uno plugin version> -p:UnoIsDebug=false
21+
```
22+
23+
To retrieve the HTTP response content (MVID and target framework info), use -getProperty:
24+
25+
```shell
26+
dotnet build <app csproj> -c:Debug -t:UnoNotifyAppLaunch -getProperty:UnoNotifyAppLaunchHttpResponse -p:TargetFramework=<tfm> -p:NoBuild=true -restore:false -p:UnoIde=<ide> -p:UnoPlugin=<version> -p:UnoIsDebug=false
27+
```
28+
-->
29+
30+
<PropertyGroup>
31+
<UnoNotifyAppLaunchDependsOn>$(UnoNotifyAppLaunchDependsOn);GetTargetPath</UnoNotifyAppLaunchDependsOn>
32+
</PropertyGroup>
33+
34+
<UsingTask TaskName="Uno.Sdk.Tasks.UnoNotifyAppLaunchToDevServer_v0"
35+
AssemblyFile="$(MSBuildThisFileDirectory)netstandard2.0\Uno.Sdk_v0.dll" />
36+
<Target Name="UnoNotifyAppLaunch"
37+
DependsOnTargets="$(UnoNotifyAppLaunchDependsOn)"
38+
Condition="'$(TargetFramework)'!=''">
39+
40+
<Message Importance="Low"
41+
Text="NotifyAppLaunch: TFM=$(TargetFramework) TargetPath=$(TargetPath) Configuration=$(Configuration)"/>
42+
43+
<Error Text="UnoNotifyAppLaunch target should only be called with Debug configuration. Current configuration: $(Configuration)"
44+
Condition="'$(Configuration)' != 'Debug'"/>
45+
46+
<Error Text="UnoRemoteControlPort property must be defined to use UnoNotifyAppLaunch target."
47+
Condition="'$(UnoRemoteControlPort)' == ''"/>
48+
49+
<Error Text="Target assembly file not found: $(TargetPath)"
50+
Condition="!Exists('$(TargetPath)')"/>
51+
52+
<!-- Pass the new properties -->
53+
<UnoNotifyAppLaunchToDevServer_v0
54+
Port="$(UnoRemoteControlPort)"
55+
TargetPath="$(TargetPath)"
56+
Ide="$(UnoIde)"
57+
Plugin="$(UnoPlugin)"
58+
IsDebug="$(UnoIsDebug)">
59+
<Output TaskParameter="Success" PropertyName="_UnoLaunchSuccess"/>
60+
<Output TaskParameter="ResponseContent" PropertyName="UnoNotifyAppLaunchHttpResponse"/>
61+
</UnoNotifyAppLaunchToDevServer_v0>
62+
63+
<Error Text="Failed to notify devserver for assembly: $(TargetPath)" Importance="High" Condition="'$(_UnoLaunchSuccess)' != 'True'"/>
64+
65+
<Message Text="Successfully notified devserver for assembly: $(TargetPath)"
66+
Condition="'$(_UnoLaunchSuccess)' == 'True'"/>
67+
68+
<Message Text="DevServer response: $(UnoNotifyAppLaunchHttpResponse)"
69+
Condition="'$(_UnoLaunchSuccess)' == 'True' AND '$(UnoNotifyAppLaunchHttpResponse)' != ''"/>
70+
</Target>
71+
72+
</Project>

src/Uno.Sdk/targets/Uno.Build.targets

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,7 @@
183183

184184
<Import Project="Uno.SingleProject.VS.Build.targets"
185185
Condition=" '$(UnoSingleProject)' == 'true' AND '$(BuildingInsideVisualStudio)' == 'true' " />
186+
187+
<!-- App Launch notification target for IDE integrations -->
188+
<Import Project="Uno.AppLaunch.targets" />
186189
</Project>

src/Uno.UI-Skia-only.slnf

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
{
1+
{
22
"solution": {
33
"path": "Uno.UI.slnx",
44
"projects": [
55
"AddIns\\Uno.UI.Foldable\\Uno.UI.Foldable.netcoremobile.csproj",
66
"AddIns\\Uno.UI.GooglePlay\\Uno.UI.GooglePlay.netcoremobile.csproj",
7+
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.netcoremobile.csproj",
78
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.Reference.csproj",
89
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.Skia.csproj",
910
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.Tests.csproj",
1011
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.Wasm.csproj",
11-
"AddIns\\Uno.UI.Lottie\\Uno.UI.Lottie.netcoremobile.csproj",
12-
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Reference.csproj",
13-
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Skia.csproj",
14-
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Wasm.csproj",
15-
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.netcoremobile.csproj",
1612
"AddIns\\Uno.UI.Maps\\Uno.UI.Maps.netcoremobile.csproj",
1713
"AddIns\\Uno.UI.MediaPlayer.Skia.Win32\\Uno.UI.MediaPlayer.Skia.Win32.csproj",
1814
"AddIns\\Uno.UI.MediaPlayer.Skia.X11\\Uno.UI.MediaPlayer.Skia.X11.csproj",
1915
"AddIns\\Uno.UI.MediaPlayer.WebAssembly\\Uno.UI.MediaPlayer.WebAssembly.csproj",
16+
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.netcoremobile.csproj",
17+
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Reference.csproj",
18+
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Skia.csproj",
19+
"AddIns\\Uno.UI.MSAL\\Uno.UI.MSAL.Wasm.csproj",
20+
"AddIns\\Uno.UI.Svg\\Uno.UI.Svg.netcoremobile.csproj",
2021
"AddIns\\Uno.UI.Svg\\Uno.UI.Svg.Reference.csproj",
2122
"AddIns\\Uno.UI.Svg\\Uno.UI.Svg.Skia.csproj",
22-
"AddIns\\Uno.UI.Svg\\Uno.UI.Svg.netcoremobile.csproj",
2323
"AddIns\\Uno.UI.WebView.Skia.X11\\Uno.UI.WebView.Skia.X11.csproj",
2424
"AddIns\\Uno.WinUI.Graphics2DSK\\Uno.WinUI.Graphics2DSK.Crossruntime.csproj",
2525
"AddIns\\Uno.WinUI.Graphics3DGL\\Uno.WinUI.Graphics3DGL.csproj",
2626
"SamplesApp\\Benchmarks.Shared\\SamplesApp.Benchmarks.shproj",
2727
"SamplesApp\\SamplesApp.Shared\\SamplesApp.Shared.shproj",
2828
"SamplesApp\\SamplesApp.Skia.Generic\\SamplesApp.Skia.Generic.csproj",
29-
"SamplesApp\\SamplesApp.Skia.WebAssembly.Browser\\SamplesApp.Skia.WebAssembly.Browser.csproj",
3029
"SamplesApp\\SamplesApp.Skia.netcoremobile\\SamplesApp.Skia.netcoremobile.csproj",
30+
"SamplesApp\\SamplesApp.Skia.WebAssembly.Browser\\SamplesApp.Skia.WebAssembly.Browser.csproj",
3131
"SamplesApp\\SamplesApp.Skia\\SamplesApp.Skia.csproj",
3232
"SamplesApp\\SamplesApp.UITests.Generator\\Uno.Samples.UITest.Generator.csproj",
3333
"SamplesApp\\SamplesApp.UITests\\SamplesApp.UITests.csproj",
@@ -46,27 +46,29 @@
4646
"SourceGenerators\\Uno.UI.Tasks\\Uno.UI.Tasks.csproj",
4747
"Uno.Foundation.Logging\\Uno.Foundation.Logging.csproj",
4848
"Uno.Foundation.Runtime.WebAssembly\\Uno.Foundation.Runtime.WebAssembly.csproj",
49+
"Uno.Foundation\\Uno.Foundation.netcoremobile.csproj",
4950
"Uno.Foundation\\Uno.Foundation.Reference.csproj",
5051
"Uno.Foundation\\Uno.Foundation.Skia.csproj",
5152
"Uno.Foundation\\Uno.Foundation.Wasm.csproj",
52-
"Uno.Foundation\\Uno.Foundation.netcoremobile.csproj",
5353
"Uno.Sdk\\Uno.Sdk.csproj",
5454
"Uno.UI.Adapter.Microsoft.Extensions.Logging\\Uno.UI.Adapter.Microsoft.Extensions.Logging.csproj",
5555
"Uno.UI.Composition\\Uno.UI.Composition.Reference.csproj",
5656
"Uno.UI.Composition\\Uno.UI.Composition.Skia.csproj",
5757
"Uno.UI.DevServer.Cli\\Uno.UI.DevServer.Cli.csproj",
58+
"Uno.UI.Dispatching\\Uno.UI.Dispatching.netcoremobile.csproj",
5859
"Uno.UI.Dispatching\\Uno.UI.Dispatching.Reference.csproj",
5960
"Uno.UI.Dispatching\\Uno.UI.Dispatching.Skia.csproj",
6061
"Uno.UI.Dispatching\\Uno.UI.Dispatching.Wasm.csproj",
61-
"Uno.UI.Dispatching\\Uno.UI.Dispatching.netcoremobile.csproj",
6262
"Uno.UI.FluentTheme.v1\\Uno.UI.FluentTheme.v1.Skia.csproj",
6363
"Uno.UI.FluentTheme.v2\\Uno.UI.FluentTheme.v2.Skia.csproj",
6464
"Uno.UI.FluentTheme\\Uno.UI.FluentTheme.Skia.csproj",
6565
"Uno.UI.RemoteControl.Controller\\Uno.UI.RemoteControl.Controller.csproj",
66+
"Uno.UI.RemoteControl.DevServer.Tests\\Uno.UI.RemoteControl.DevServer.Tests.csproj",
6667
"Uno.UI.RemoteControl.Host\\Uno.UI.RemoteControl.Host.csproj",
6768
"Uno.UI.RemoteControl.Messaging\\Uno.UI.RemoteControl.Messaging.csproj",
6869
"Uno.UI.RemoteControl.Server.Processors\\Uno.UI.RemoteControl.Server.Processors.csproj",
6970
"Uno.UI.RemoteControl.Server\\Uno.UI.RemoteControl.Server.csproj",
71+
"Uno.UI.RemoteControl.TestProcessor\\Uno.UI.RemoteControl.TestProcessor.csproj",
7072
"Uno.UI.RemoteControl.VS\\Uno.UI.RemoteControl.VS.csproj",
7173
"Uno.UI.RemoteControl\\Uno.UI.RemoteControl.Skia.csproj",
7274
"Uno.UI.Runtime.Skia.Android\\Uno.UI.Runtime.Skia.Android.csproj",
@@ -87,10 +89,10 @@
8789
"Uno.UI\\Uno.UI.Reference.csproj",
8890
"Uno.UI\\Uno.UI.Skia.csproj",
8991
"Uno.UWPSyncGenerator\\Uno.UWPSyncGenerator.csproj",
92+
"Uno.UWP\\Uno.netcoremobile.csproj",
9093
"Uno.UWP\\Uno.Reference.csproj",
9194
"Uno.UWP\\Uno.Skia.csproj",
92-
"Uno.UWP\\Uno.Wasm.csproj",
93-
"Uno.UWP\\Uno.netcoremobile.csproj"
95+
"Uno.UWP\\Uno.Wasm.csproj"
9496
]
9597
}
9698
}

src/Uno.UI-UnitTests-only.slnf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
"Uno.UI.FluentTheme.v1\\Uno.UI.FluentTheme.v1.Tests.csproj",
2121
"Uno.UI.FluentTheme.v2\\Uno.UI.FluentTheme.v2.Tests.csproj",
2222
"Uno.UI.FluentTheme\\Uno.UI.FluentTheme.Tests.csproj",
23+
"Uno.UI.RemoteControl.DevServer.Tests\\Uno.UI.RemoteControl.DevServer.Tests.csproj",
2324
"Uno.UI.Tests.ViewLibraryProps\\Uno.UI.Tests.ViewLibraryProps.csproj",
2425
"Uno.UI.Tests.ViewLibrary\\Uno.UI.Tests.ViewLibrary.csproj",
2526
"Uno.UI.Tests\\Uno.UI.Unit.Tests.csproj",
2627
"Uno.UI.Toolkit\\Uno.UI.Toolkit.Tests.csproj",
2728
"Uno.UI\\Uno.UI.Tests.csproj",
28-
"Uno.UWP\\Uno.Tests.csproj",
29-
"Uno.UI.RemoteControl.DevServer.Tests\\Uno.UI.RemoteControl.DevServer.Tests.csproj"
29+
"Uno.UWP\\Uno.Tests.csproj"
3030
]
3131
}
32-
}
32+
}

0 commit comments

Comments
 (0)