Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Post a report when normally termination #93

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Editor/Localization/ja.po
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,27 @@ msgstr "@hereをメッセージにつける"
# addHereInSlackMessage tooltip
msgid "Whether adding @here into Slack messages or not"
msgstr "Slack通知メッセージに@hereを付けます"

# withScreenshotOnError
msgid "Take screenshot"
msgstr "スクリーンショット"

# withScreenshotOnError tooltip
msgid "Take a screenshot when posting an error terminated report"
msgstr "エラー終了時にスクリーンショットを撮影します"

# postOnNormally
msgid "Normally terminated report"
msgstr "正常終了時にもレポート"

# postOnNormally tooltip
msgid "Post a report if normally terminates."
msgstr "正常終了時にもレポートをポストします"

# withScreenshotOnNormally (same as withScreenshotOnError)
msgid "Take screenshot"
msgstr "スクリーンショット"

# withScreenshotOnNormally tooltip
msgid "Take a screenshot when posting a normally terminated report"
msgstr "正常終了時にスクリーンショットを撮影します"
43 changes: 35 additions & 8 deletions Editor/UI/Reporters/SlackReporterEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ namespace DeNA.Anjin.Editor.UI.Reporters
public class SlackReporterEditor : UnityEditor.Editor
{
private const float SpacerPixels = 10f;
private const float SpacerPixelsUnderHeader = 4f;


private static readonly string s_description = L10n.Tr("Description");
private static readonly string s_descriptionTooltip = L10n.Tr("Description about this Reporter instance");
private SerializedProperty _descriptionProp;
Expand All @@ -31,8 +30,6 @@ public class SlackReporterEditor : UnityEditor.Editor
private SerializedProperty _slackChannelsProp;
private GUIContent _slackChannelsGUIContent;

private static readonly string s_slackMentionSettingsHeader = L10n.Tr("Slack Mention Settings");

private static readonly string s_mentionSubTeamIDs = L10n.Tr("Sub Team IDs to Mention");
private static readonly string s_mentionSubTeamIDsTooltip = L10n.Tr("Sub team IDs to mention (comma separates)");
private SerializedProperty _mentionSubTeamIDsProp;
Expand All @@ -43,6 +40,20 @@ public class SlackReporterEditor : UnityEditor.Editor
private SerializedProperty _addHereInSlackMessageProp;
private GUIContent _addHereInSlackMessageGUIContent;

private static readonly string s_withScreenshotOnError = L10n.Tr("Take screenshot");
private static readonly string s_withScreenshotOnErrorTooltip = L10n.Tr("Take a screenshot when posting an error terminated report");
private SerializedProperty _withScreenshotOnErrorProp;
private GUIContent _withScreenshotOnErrorGUIContent;

private static readonly string s_postOnNormally = L10n.Tr("Normally terminated report");
private static readonly string s_postOnNormallyTooltip = L10n.Tr("Post a report if normally terminates.");
private SerializedProperty _postOnNormallyProp;
private GUIContent _postOnNormallyGUIContent;

private static readonly string s_withScreenshotOnNormally = L10n.Tr("Take screenshot");
private static readonly string s_withScreenshotOnNormallyTooltip = L10n.Tr("Take a screenshot when posting a normally terminated report");
private SerializedProperty _withScreenshotOnNormallyProp;
private GUIContent _withScreenshotOnNormallyGUIContent;

private void OnEnable()
{
Expand All @@ -66,26 +77,42 @@ private void Initialize()

_addHereInSlackMessageProp = serializedObject.FindProperty(nameof(SlackReporter.addHereInSlackMessage));
_addHereInSlackMessageGUIContent = new GUIContent(s_addHereInSlackMessage, s_addHereInSlackMessageTooltip);

_withScreenshotOnErrorProp = serializedObject.FindProperty(nameof(SlackReporter.withScreenshotOnError));
_withScreenshotOnErrorGUIContent = new GUIContent(s_withScreenshotOnError, s_withScreenshotOnErrorTooltip);

_postOnNormallyProp = serializedObject.FindProperty(nameof(SlackReporter.postOnNormally));
_postOnNormallyGUIContent = new GUIContent(s_postOnNormally, s_postOnNormallyTooltip);

_withScreenshotOnNormallyProp = serializedObject.FindProperty(nameof(SlackReporter.withScreenshotOnNormally));
_withScreenshotOnNormallyGUIContent = new GUIContent(s_withScreenshotOnNormally, s_withScreenshotOnNormallyTooltip);
}


public override void OnInspectorGUI()
{
serializedObject.Update();

EditorGUILayout.PropertyField(_descriptionProp, _descriptionGUIContent);
GUILayout.Space(SpacerPixels);

EditorGUILayout.PropertyField(_slackTokenProp, _slackTokenGUIContent);
EditorGUILayout.PropertyField(_slackChannelsProp, _slackChannelsGUIContent);

GUILayout.Space(SpacerPixels);
GUILayout.Label(s_slackMentionSettingsHeader);
GUILayout.Space(SpacerPixelsUnderHeader);

EditorGUILayout.PropertyField(_mentionSubTeamIDsProp, _mentionSubTeamIDsGUIContent);
EditorGUILayout.PropertyField(_addHereInSlackMessageProp, _addHereInSlackMessageGUIContent);

GUILayout.Space(SpacerPixels);
EditorGUILayout.PropertyField(_withScreenshotOnErrorProp, _withScreenshotOnErrorGUIContent);

GUILayout.Space(SpacerPixels);
EditorGUILayout.PropertyField(_postOnNormallyProp, _postOnNormallyGUIContent);

EditorGUI.BeginDisabledGroup(!_postOnNormallyProp.boolValue);
EditorGUILayout.PropertyField(_withScreenshotOnNormallyProp, _withScreenshotOnNormallyGUIContent);
EditorGUI.EndDisabledGroup();

serializedObject.ApplyModifiedProperties();
}
}
Expand Down
2 changes: 1 addition & 1 deletion Editor/UI/Settings/AutopilotSettingsEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ internal void Stop()
var autopilot = FindObjectOfType<Autopilot>();
if (autopilot)
{
autopilot.TerminateAsync(ExitCode.Normally).Forget();
autopilot.TerminateAsync(ExitCode.Normally, reporting: false).Forget();
}
}

Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ This item can also be overridden from the commandline (see below).
<dt>Time Scale</dt><dd>Time.timeScale. Default is 1.0</dd>
<dt>JUnit Report Path</dt><dd>Specifies the JUnit format report file output path (optional). If there are zero errors and zero failures, the autopilot run is considered to have completed successfully. </dd>
<dt>Logger</dt><dd>Logger used for this autopilot settings. If omitted, <code>Debug.unityLogger</code> will be used as default.</dd>
<dt>Reporter</dt><dd>Reporter that called when some errors occurred in target application</dd>
<dt>Reporter</dt><dd>Reporter to be called on Autopilot terminate.</dd>
</dl>

#### Error Handling Settings
Expand Down Expand Up @@ -461,12 +461,15 @@ The instance of this Reporter (.asset file) can have the following settings.
The bot must be invited to the channel.</dd>
<dt>Mention Sub Team IDs</dt><dd>Comma Separated Team IDs to mention in notification message</dd>
<dt>Add Here In Slack Message</dt><dd>Add @here to notification message. Default is off</dd>
<dt>Take screenshot</dt><dd>Take a screenshot when posting an error terminated report. Default is on</dd>
<dt>Normally terminated report</dt><dd>Post a report if normally terminates. Default is off</dd>
<dt>Take screenshot</dt><dd>Take a screenshot when posting a normally terminated report. Default is off</dd>
</dl>

You can create a bot on the following page:
You can create a Slack Bot on the following page:
[Slack API: Applications](https://api.slack.com/apps)

The bot needs the following permissions:
The Slack Bot needs the following permissions:

- chat:write
- files:write
Expand Down
11 changes: 7 additions & 4 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ v1.0.0時点では `EmergencyExitAgent` の使用を想定しています。
<dt>Time Scale</dt><dd>Time.timeScaleを指定します。デフォルトは1.0</dd>
<dt>JUnit Report Path</dt><dd>JUnit形式のレポートファイル出力パスを指定します(省略可)。オートパイロット実行の成否は、Unityエディターの終了コードでなくこのファイルを見て判断するのが確実です。errors, failuresともに0件であれば正常終了と判断できます。</dd>
<dt>Logger</dt><dd>オートパイロットが使用するLogger指定します。省略時は <code>Debug.unityLogger</code> がデフォルトとして使用されます</dd>
<dt>Reporter</dt><dd>対象のアプリケーションで発生したエラーを通知するReporterを指定します</dd>
<dt>Reporter</dt><dd>オートパイロット終了時に通知を行なうReporterを指定します</dd>
</dl>

#### エラーハンドリング設定
Expand Down Expand Up @@ -464,13 +464,16 @@ Slackにレポート送信するReporterです。
コマンドライン引数 <code>-SLACK_CHANNELS</code> で上書きできます。
チャンネルにはBotを招待しておく必要があります。</dd>
<dt>Mention Sub Team IDs</dt><dd>通知メッセージでメンションするチームのIDをカンマ区切りで指定します</dd>
<dt>Add Here In Slack Message</dt><dd>通知メッセージに@hereを付けます。デフォルトはoff</dd>
<dt>Add Here In Slack Message</dt><dd>通知メッセージに@hereを付けます(デフォルト: off)</dd>
<dt>Take screenshot</dt><dd>エラー終了時にスクリーンショットを撮影します(デフォルト: on)</dd>
<dt>Normally terminated report</dt><dd>正常終了時にもレポートをポストします(デフォルト: off)</dd>
<dt>Take screenshot</dt><dd>正常終了時にスクリーンショットを撮影します(デフォルト: off)</dd>
</dl>

Botは次のページで作成できます。
Slack Botは次のページで作成できます。
[Slack API: Applications](https://api.slack.com/apps)

Botには次の権限が必要です。
Slack Botには次の権限が必要です。

- chat:write
- files:write
Expand Down
60 changes: 40 additions & 20 deletions Runtime/Autopilot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,28 @@

namespace DeNA.Anjin
{
/// <summary>
/// Can be terminated.
/// </summary>
public interface ITerminatable
{
/// <summary>
/// Terminate autopilot
/// </summary>
/// <param name="exitCode">Exit code for Unity Editor/ Player-build</param>
/// <param name="message">Log message string or terminate message</param>
/// <param name="stackTrace">Stack trace when terminate by the log message</param>
/// <param name="reporting">Call Reporter if true</param>
/// <param name="token">Cancellation token</param>
/// <returns>A task awaits termination get completed</returns>
UniTask TerminateAsync(ExitCode exitCode, string message = null, string stackTrace = null,
bool reporting = true, CancellationToken token = default);
}

/// <summary>
/// Autopilot main logic
/// </summary>
public class Autopilot : MonoBehaviour
public class Autopilot : MonoBehaviour, ITerminatable
{
private AbstractLoggerAsset _loggerAsset;
private ILogger _logger;
Expand All @@ -28,6 +46,7 @@ public class Autopilot : MonoBehaviour
private AutopilotState _state;
private AutopilotSettings _settings;
private float _startTime;
private bool _isTerminating;

private void Start()
{
Expand All @@ -51,7 +70,7 @@ private void Start()
// NOTE: Registering logMessageReceived must be placed before DispatchByScene.
// Because some agent can throw an error immediately, so reporter can miss the error if
// registering logMessageReceived is placed after DispatchByScene.
_logMessageHandler = new LogMessageHandler(_settings, _settings.reporter);
_logMessageHandler = new LogMessageHandler(_settings, this);

_dispatcher = new AgentDispatcher(_settings, _logger, _randomFactory);
var dispatched = _dispatcher.DispatchByScene(SceneManager.GetActiveScene(), false);
Expand Down Expand Up @@ -126,34 +145,41 @@ internal static void ConvertSlackReporterFromObsoleteSlackSettings(AutopilotSett
private IEnumerator Lifespan(int timeoutSec)
{
yield return new WaitForSecondsRealtime(timeoutSec);
yield return UniTask.ToCoroutine(() => TerminateAsync(ExitCode.Normally));
yield return UniTask.ToCoroutine(() =>
TerminateAsync(ExitCode.Normally, "Autopilot has reached the end of its lifespan."));
}

private void OnDestroy()
{
// Clear event listeners.
// When play mode is stopped by the user, onDestroy calls without TerminateAsync.

_logger?.Log("Destroy Autopilot object");
_dispatcher?.Dispose();
_logMessageHandler?.Dispose();
_settings.loggerAsset?.Dispose();
}

/// <summary>
/// Terminate autopilot
/// </summary>
/// <param name="exitCode">Exit code for Unity Editor</param>
/// <param name="logString">Log message string when terminate by the log message</param>
/// <param name="stackTrace">Stack trace when terminate by the log message</param>
/// <param name="token">Cancellation token</param>
/// <returns>A task awaits termination get completed</returns>
public async UniTask TerminateAsync(ExitCode exitCode, string logString = null, string stackTrace = null,
CancellationToken token = default)
/// <inheritdoc/>
public async UniTask TerminateAsync(ExitCode exitCode, string message = null, string stackTrace = null,
bool reporting = true, CancellationToken token = default)
{
if (_isTerminating)
{
return; // Prevent multiple termination.
}

_isTerminating = true;

if (reporting && _state.IsRunning && _settings.reporter != null)
{
await _settings.reporter.PostReportAsync(message, stackTrace, exitCode, token);
}

if (_state.settings != null && !string.IsNullOrEmpty(_state.settings.junitReportPath))
{
var time = Time.realtimeSinceStartup - _startTime;
JUnitReporter.Output(_state.settings.junitReportPath, (int)exitCode, logString, stackTrace, time);
JUnitReporter.Output(_state.settings.junitReportPath, (int)exitCode, message, stackTrace, time);
}

DestroyImmediate(this.gameObject);
Expand All @@ -162,12 +188,6 @@ public async UniTask TerminateAsync(ExitCode exitCode, string logString = null,
await Launcher.TeardownLaunchAutopilotAsync(_state, _logger, exitCode, "Autopilot", token);
}

/// <summary>
/// Terminate autopilot
/// </summary>
/// <param name="exitCode">Exit code for Unity Editor</param>
/// <param name="logString">Log message string when terminate by the log message</param>
/// <param name="stackTrace">Stack trace when terminate by the log message</param>
[Obsolete("Use " + nameof(TerminateAsync))]
public void Terminate(ExitCode exitCode, string logString = null, string stackTrace = null)
{
Expand Down
15 changes: 7 additions & 8 deletions Runtime/Reporters/AbstractReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
namespace DeNA.Anjin.Reporters
{
/// <summary>
/// Reporter base class
/// Reporter base class.
/// Reporter is called on Autopilot termination.
/// </summary>
public abstract class AbstractReporter : ScriptableObject
{
Expand All @@ -22,17 +23,15 @@ public abstract class AbstractReporter : ScriptableObject
/// <summary>
/// Post report log message, stacktrace and screenshot
/// </summary>
/// <param name="logString">Log message</param>
/// <param name="stackTrace">Stack trace</param>
/// <param name="type">Log message type</param>
/// <param name="withScreenshot">With screenshot</param>
/// <param name="message">Log message or terminate message</param>
/// <param name="stackTrace">Stack trace (can be null)</param>
/// <param name="exitCode">Exit code indicating the reason for termination</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns></returns>
public abstract UniTask PostReportAsync(
string logString,
string message,
string stackTrace,
LogType type,
bool withScreenshot,
ExitCode exitCode,
CancellationToken cancellationToken = default
);
}
Expand Down
7 changes: 3 additions & 4 deletions Runtime/Reporters/CompositeReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@ public class CompositeReporter : AbstractReporter

/// <inheritdoc />
public override async UniTask PostReportAsync(
string logString,
string message,
string stackTrace,
LogType type,
bool withScreenshot,
ExitCode exitCode,
CancellationToken cancellationToken = default
)
{
await UniTask.WhenAll(
reporters
.Where(r => r != this && r != null)
.Select(
r => r.PostReportAsync(logString, stackTrace, type, withScreenshot, cancellationToken)
r => r.PostReportAsync(message, stackTrace, exitCode, cancellationToken)
)
);
}
Expand Down
Loading