Appearance
Implementing Custom Incident Reports
This document describes how to report incidents and implement custom incident report types.
Appearance
This document describes how to report incidents and implement custom incident report types.
The Metaplay SDK provides a powerful built-in incident report system that should cover most cases where you would want to record problems on the client, including networking issues, unhandled exceptions, and more. Furthermore, the SDK also supports creating and using custom incident report types.
To implement a custom incident report type, start by creating a class in shared code that derives from PlayerIncidentReport
. The combination of Type
, SubType
, and GetReason
are used to calculate the "fingerprint" of the incident, which is used to group the incidents in the LiveOps Dashboard's Unique Incidents view. The Type
field is also used for manual filtering on the LiveOps Dashboard. The following example can be found in the Idler sample:
[MetaSerializableDerived(101)]
public class HandledErrorPlayerIncident : PlayerIncidentReport
{
[MetaMember(1)] public string ExceptionType;
[MetaMember(2)] public string ExceptionMessage;
[MetaMember(3)] public string StackTrace;
// Type of incident, usually the name of the C# type.
public override string Type => nameof(HandledErrorPlayerIncident);
// Subtype of the incident, for example, the name of the error that prompted the incident. Shown together with the Type in the dashboard incident details page.
public override string SubType => ExceptionType;
// Reason for the incident, for example, the message of the error that prompted the incident. Shown as a preview of the incident on the dashboard incident list view.
// Used together with Type and Subtype to calculate the "fingerprint" of the incident to use for grouping incidents on the dashboard unique incidents view.
public override string GetReason()
{
return ExceptionMessage;
}
...
}
Additionally, to tell the LiveOps Dashboard what to show, you need to implement the GetIncidentDashboardInfo()
method. You can use the IncidentDashboardInfo.PreFillDashboardInfo()
method to fill in the base class info:
public class MyCustomPlayerIncident : PlayerIncidentReport
{
...
public override IncidentDashboardInfo GetIncidentDashboardInfo()
{
// Use the PreFillDashboardInfo() method to collect the incident's base class members into the dashboardInfo
IncidentDashboardInfo dashboardInfo = IncidentDashboardInfo.PreFillDashboardInfo(this);
// Optionally populate the ErrorInfo member to visualize errors and stacktraces neatly
dashboardInfo.ErrorInfo = new IncidentDashboardInfo.ErrorDashboardInfo() { ... };
// Optionally populate other members of the IncidentDashboardInfo class to visualize them neatly on the LiveOps dashboard
dashboardInfo.NetworkInfo = new IncidentDashboardInfo.NetworkDashboardInfo() { ... };
dashboardInfo.PlayerModelDiffReport = new IncidentDashboardInfo.PlayerModelDiffReportInfo() { ... };
dashboardInfo.ExtraInfo = ... ; // See below for more on this!
return dashboardInfo;
}
}
The LiveOps Dashboard will automatically show any additional data passed to the ExtraInfo
member using the generated UI system. The data will show up in the "Extra Info" tab of the incident report detail view. To utilize this feature and visualize extra information, define a class deriving from IncidentDashboardInfo.ExtraIncidentDashboardInfo
and populate it in the override of the GetIncidentDashboardInfo()
method. See the example:
[MetaSerializableDerived(101)]
public class HandledErrorPlayerIncident : PlayerIncidentReport
{
...
[MetaMember(4)] public string DebugInfo;
// The extra info class can be used to automatically visualize extra info on the LiveOps dashboard using the generated UI system.
// For simplicity, you can use the same tag ID as for the incident report.
[MetaSerializableDerived(101)]
public class ExtraDashboardInfo : IncidentDashboardInfo.ExtraIncidentDashboardInfo
{
public string DebugInfo;
}
public override IncidentDashboardInfo GetIncidentDashboardInfo()
{
...
// Optionally populate the ExtraInfo member with additional info that you also want to visualize on the dashboard.
dashboardInfo.ExtraInfo = new ExtraDashboardInfo()
{
DebugInfo = DebugInfo,
};
return dashboardInfo;
}
}
Now when viewing an incident report of our custom type on the LiveOps Dashboard, we see the extra info we supplied!
To report an incident and add an associated analytics event, use MetaplaySDK.IncidentTracker.AddIncidentAndAnalyticsEvent()
, or MetaplaySDK.IncidentTracker.AddIncidentWithNetworkReportAndAnalyticsEvent()
if you want to include a network diagnostic report. Note that the IncidentTracker
can throttle incident reports if they happen too frequently (more than 5 per minute by default). You can bypass the throttling by setting the parameter useThrottle
to false. Alternatively, you can directly add an incident report to the IncidentRepository
using MetaplaySDK.IncidentRepository.AddOrUpdate()
. This will add the incident report without the associated analytics event.
Use IncidentTracker.GetSharedIncidentInfo()
to populate the report with commonly needed info about the application, system, and client logs.
public static void ReportHandledErrorPlayerIncident(Exception ex, string debugInfo)
{
MetaplaySDK.IncidentTracker.AddIncidentAndAnalyticsEvent(
new HandledErrorPlayerIncident(
MetaplaySDK.IncidentTracker.GetSharedIncidentInfo(),
ex.GetType().ToString(),
ex.Message,
ex.StackTrace,
debugInfo
));
}
If you need to create a player incident from the server, you can directly persist it to the database as follows:
// Persist an incident directly on the server and inform the PlayerActor
await PlayerIncidentStorage.SerializeAndPersistIncidentAsync(playerId, incidentReport, informPlayerActor: true, sourceEntity: this);
By default, 100% of incidents of a given type are uploaded to the server. You can reduce the upload percentage of certain types of incidents by overriding it in the runtime options:
## Change HandledErrorPlayerIncident type's upload percentage to 50%
PlayerIncident:
UploadPercentagesPerIncidentType:
"HandledErrorPlayerIncident": 50