Adaptivist ScriptRunnerへのログイン

この記事では、Adaptivist ScriptRunnerへのログインについて説明します。 ログは、産業環境での緊急事態を分析するための開発者および運用担当者にとっての主要なツールです。 したがって、アプリケーションを開発するときは、インシデントを分析するときに必要なものについて考える必要があります。



画像



すべての例は、Jira Software 7.8.0およびAdaptivist ScriptRunner 5.3.9で準備されました。



しばらくの間進行しているプロジェクトに来た、またはすでに何十ものスクリプトを書いたと仮定してみましょう。エラーが発生したインシデントがあります。



インシデントの例1



2018-03-17 11:43:04,891 http-nio-8080-exec-17 ERROR admin 703x1504x1 16n2j3n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] Script function failed on issue: BP-2, actionId: 951, file: <inline script> java.lang.NullPointerException: Cannot invoke method getKey() on null object at Script1.run(Script1.groovy:4)
      
      





ログから、これがインラインスクリプトであることは明らかです。 スクリプトはid 951からの移行中のある種のビジネスプロセスにあります。次に、id 951からの移行に関して運用担当者に情報を要求する必要があります。この情報を取得した後でも、このスクリプトがバリデーターまたはポスト関数に従います。 さらに、ある種のオブジェクトがヌルであるためにNPEが取得されたことがわかりますが、それがどのようなオブジェクトで、なぜヌルであったかは想像できません。 最初にこのインラインスクリプトを見つける必要があり、これには問題があります。



インシデントの例2



 2018-03-17 17:07:31,325 http-nio-8080-exec-22 ERROR admin 1027x4968x1 gj4xqt 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] Script function failed on issue: BP-2, actionId: 961, file: ru/matveev/alexey/main/postfunctions/pfNPE.groovy java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java_util_List$get$6.call(Unknown Source) at ru.matveev.UtilHelper.getIndexOutOfBoundsException(UtilHelper.groovy:13) at ru.matveev.UtilHelper$getIndexOutOfBoundsException.call(Unknown Source) at ru.matveev.alexey.main.postfunctions.pfNPE.run(pfNPE.groovy:10)
      
      





物事はここで良いです。 pfNPE.groovyスクリプトが原因でgetIndexOutOfBoundsExceptionメソッドにエラーがあることがわかります。 このスクリプトの場所はわかっています。 ただし、UtilHelper.groovyファイルの場所はわかりません。 ファイルシステムで検索を実行し、おそらくUtilHelperファイルを見つけることができますが、IndexOutOfBoundsExceptionを取得した理由、要素を選択した配列の値を理解できません。 つまり、スクリプトが実行されたコンテキストが不足しています。



インシデントの例3



 2018-03-17 16:26:25,165 http-nio-8080-exec-22 ERROR admin 986x4387x1 gj4xqt 127.0.0.1 /secure/CommentAssignIssue.jspa [coscjira.utils.ConditionUtils] Condition failed on issue: BP-2, built-in script:com.onresolve.scriptrunner.canned.jira.workflow.postfunctions.CreateSubTask java.lang.NullPointerException: Cannot invoke method getClauseNames() on null object at Script19.run(Script19.groovy:3) 2018-03-17 16:26:25,166 http-nio-8080-exec-22 ERROR admin 986x4387x1 gj4xqt 127.0.0.1 /secure/CommentAssignIssue.jspa [coscjira.utils.ConditionUtils] Script follows: import com.atlassian.jira.component.ComponentAccessor def cs = ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("field name") cs.getClauseNames()
      
      





ここで、サブタスクを作成するScriptRunner組み込み関数でエラーが発生したことがわかります。 しかし、この関数がpost関数、リスナー、または他の場所に追加されたことはわかりません。 さらに、スクリプト実行コンテキストもありません。



インシデントの例4



       .      .
      
      





カスタムスクリプトがメールを送信していることがわかっていると仮定しますが、このカスタムスクリプトの何が問題なのかをどのように理解するのでしょうか。



それでは、ログに表示される情報を体系化して、次のことを試してみましょう。





1.ファイルシステムにすべてのスクリプトを構成する必要があります。



オブジェクトの種類ごとにスクリプトを保存することを好みます。 たとえば、関数の投稿はru / matveev / alexey / main / postfunctionsディレクトリに保存され、リスナーはru / matveev / alexey / main / listenersなどに保存されます。 これは、スクリプトのレイアウトを構造化し、さまざまなスクリプトをすばやくナビゲートするのに役立ちます(この構造化が不便な例外があります。別の記事で説明します)。



2.スクリプトパッケージを最初の行として定義する必要があります



スクリプトパッケージは、ファイルシステム内の場所を決定します。 たとえば、投稿機能の場合、パッケージは次のように定義されます。



 package ru.matveev.alexey.main.postfunctions
      
      





これは、スクリプトの場所を特定するのに役立ちます。 たとえば、インシデント2では、ログに次のエントリがありました。



  at ru.matveev.UtilHelper.getIndexOutOfBoundsException(UtilHelper.groovy:13)
      
      





ここから、UtilHelperがru / matveevにあることがわかります。



3.インポート後すぐにスクリプトロガーを決定する必要があります



ScriptRunnerでスクリプトのロガーを決定する方法については、 こちらをご覧ください 。 しかし、ログ出力構文はlog4jよりも便利だと思うので、SLF4Jを使用することを好みます。 たとえば、次のようにログを記録できます。



 log.debug("var1: {}, var2: {}", var1, var2)
      
      





ここでは、スクリプトの実行中に、変数var1およびvar2の値が中括弧に置き換えられます。



ファイルシステムにあるスクリプトのロガーは、次のように定義できます。



 def log = LoggerFactory.getLogger(this.getClass())
      
      





この場合、ロガーはスクリプトパッケージとファイル名に等しくなります。 たとえば、ru.matveev.alexey.main.postfunctionsパッケージが定義されているpfNPE.groovyファイルの場合、ロガーは次のようになります。



 ru.matveev.alexey.main.postfunctions.pfNPE
      
      





インラインスクリプトの場合、ロガーを明示的に指定する必要があります。 これを行うには、インラインスクリプトを開始するときに、最初に[メモ]フィールドに入力する必要があります。 これにより、実行可能スクリプトを正確に決定できます。







次に、スクリプトがファイルシステム上にある場合、スクリプトの場所を決定します。 上記のスクリーンショットでは、リスナーを追加するため、このファイルはru / matveev / alexey / main / listenersに配置されます。 したがって、ロガーは次のようになります。



 ru.matveev.alexey.main.listeners.NPElistener
      
      





また、これがインラインスクリプトであり、ロガーの最終形式が次のようであることを理解するために、.inlineをロガーに追加します。



 def log = LoggerFactory.getLogger("ru.matveev.alexey.main.listeners.NPElistener.inline")
      
      





ロガーにより、以下が可能になります。





4.ログエントリおよび終了スクリプト



インラインスクリプトの場合、ロガーを決定した直後に、スクリプトにログインしてスクリプトを終了します。 たとえば、NoteフィールドがNPEListenerとして定義されているリスナーの場合、エントリと出口のログ行は次のようになります。



 log.info("NPElistener in") ... log.info("NPElistener out")
      
      





ビジネスプロセスで実行されるインラインスクリプトの場合、ビジネスプロセスの名前、初期ステータス、遷移、および[ノート]フィールドの値も指定できます。 たとえば、次のように:



 log.info("MyWorkflow:IN Progress:In Progress:OutOfIndexBoundsPF in") ... log.info("MyWorkflow:IN Progress:In Progress:OutOfIndexBoundsPF out")
      
      





この場合、MyWorkflowのスクリプトが呼び出され、初期ステータスはIn Progress、移行はIn Progress、値はNote OutOfIndexBoundsPFです。 呼び出しサイトのエンコード形式は任意です。



ビジネスプロセスまたはステータスの名前が変更される可能性があることは明らかであり、その情報は無関係になります。 この場合、スクリプトが遷移IDによって呼び出された場所を見つけることができます。



ファイルシステムのスクリプトの場合、入力および出力のログは次のようになります。



 log.info("{} in", this.getClass()) ... log.info("{} out", this.getClass())
      
      





ログインとログアウトは、スクリプトが作業を終了したかどうかを理解するのに役立ちます。 関数の入力と出力をスクリプトまたは呼び出されたモジュールに記録できます。 ただし、必要性を自分で判断し、ロギングのレベルを判断する必要があります。



スクリプト実行コンテキストが必要ない場合に、スクリプトからの入力と出力のみのログを有効にできるように、INFOレベルでスクリプトにログインおよびログアウトしています。 レベルは、設定されたレベル以上のすべての値を記録するように機能します。 たとえば、INFOレベルはINFO、WARN、ERROR、FATALレベルを記録しますが、DEBUGおよびTRACEは記録しません。



5.スクリプト内の変数の値を記録する必要があります



スクリプト実行のコンテキストを理解するには、少なくとも変数の値を誓約する必要があります。 変数は次のように記録されます。



  log.debug("index: {}", index)
      
      





DEBUGまたはTRACEレベルでコンテキストを記録します。



次に、上記のアプローチを使用して、インシデントを引き起こしたスクリプトを書き直して、これらのインシデントの分析にどのように役立つかを見てみましょう。



事件1



インシデント1のエラーを生成したスクリプトは次のようになります。



 import com.atlassian.jira.issue.Issue Issue issue issue.getKey()
      
      





ロギングを追加すると、スクリプトは次のようになります。



 import com.atlassian.jira.issue.Issue import org.apache.log4j.Logger def log = Logger.getLogger("ru.matveev.alexey.main.postfunctions.NPEPF.inline") log.info("MyWorkflow:IN Progress:In Progress:NPEPF in") Issue issue issue.getKey() log.info("MyWorkflow:IN Progress:In Progress:NPEPF out")
      
      





atlassian-jira.logファイルのログを表示するには、[システム]-> [ロギングとプロファイリング]に移動し、ロガーru.matveev.alexey.main.postfunctions.NPEPF.inlineのINFOレベルを設定します。 その後、ログに次のエントリが表示されます。



 2018-03-18 18:10:48,462 http-nio-8080-exec-36 INFO admin 1090x711x1 e9394m 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [rmampostfunctions.NPEPF.inline] MyWorkflow:IN Progress:In Progress:NPEPF in 2018-03-18 18:10:48,467 http-nio-8080-exec-36 ERROR admin 1090x711x1 e9394m 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] ************************************************************************************* 2018-03-18 18:10:48,468 http-nio-8080-exec-36 ERROR admin 1090x711x1 e9394m 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] Script function failed on issue: BP-2, actionId: 951, file: <inline script> java.lang.NullPointerException: Cannot invoke method getKey() on null object at Script16.run(Script16.groovy:7)
      
      





ログから、インラインNPEPFスクリプトを入力し、終了しなかったことがわかります。つまり、エラーが発生しました。 スクリプトのテキストでは、変数variableが初期化されていないことがわかります。



事件2



エラーの原因となったスクリプトは次のようになります。



pfNPE.groovy

 import ru.matveev.UtilHelper List<String> stringList = new ArrayList<>() stringList.add("0") stringList.add("1") stringList.add("2") UtilHelper.getRandomElement(stringList)
      
      





UtilHelper.groovy

 package ru.matveev import com.atlassian.jira.issue.Issue import java.util.Random public class UtilHelper { public static String getRandomElement(List<String> value) { Random rand = new Random() int max = 5 def index = rand.nextInt(max); return value.get(index) } }
      
      





ロギングを追加すると、スクリプトは次のようになります。



pfNPE.groovy

 package ru.matveev.alexey.main.postfunctions import ru.matveev.alexey.main.helpers.UtilHelper import org.slf4j.LoggerFactory; def log = LoggerFactory.getLogger(this.getClass()) log.info("pfNPE in") List<String> stringList = new ArrayList<>() stringList.add("0") stringList.add("1") stringList.add("2") UtilHelper.getRandomElement(stringList) log.info("pfNPE out")
      
      





UtilHelper.groovy

 package ru.matveev.alexey.main.helpers import com.atlassian.jira.issue.Issue import java.util.Random import org.slf4j.LoggerFactory; public class UtilHelper { private static final LOG = LoggerFactory.getLogger("ru.matveev.alexey.main.helpers.UtilHelper") public static String getRandomElement(List<String> value) { LOG.debug("getRandomElement in. value: {}", value) Random rand = new Random() int max = 5 def index = rand.nextInt(max); LOG.debug("index: {}", index) return value.get(index) } }
      
      





atlassian-jira.logファイルのログを表示するには、ru-matveev.alexey.main.helpers.UtilHelperおよびru.matveev.alexey.main.postfunctions.pfNPEをSystem-> Logging and ProfilingでDEBUGレベルに設定する必要があります。 その後、次のログが表示されます。



 2018-03-17 18:54:00,664 http-nio-8080-exec-2 INFO admin 1134x121x1 pr8c2n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [rmamain.postfunctions.pfNPE] pfNPE in 2018-03-17 18:54:00,687 http-nio-8080-exec-2 DEBUG admin 1134x121x1 pr8c2n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [rmamain.helpers.UtilHelper] getRandomElement in. value: [0, 1, 2] 2018-03-17 18:54:00,687 http-nio-8080-exec-2 DEBUG admin 1134x121x1 pr8c2n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [rmamain.helpers.UtilHelper] index: 4 2018-03-17 18:54:00,693 http-nio-8080-exec-2 ERROR admin 1134x121x1 pr8c2n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] ************************************************************************************* 2018-03-17 18:54:00,694 http-nio-8080-exec-2 ERROR admin 1134x121x1 pr8c2n 127.0.0.1 /secure/WorkflowUIDispatcher.jspa [cosjira.workflow.ScriptWorkflowFunction] Script function failed on issue: BP-2, actionId: 961, file: ru/matveev/alexey/main/postfunctions/pfNPE.groovy java.lang.IndexOutOfBoundsException: Index: 4, Size: 3 at java_util_List$get$1.call(Unknown Source) at ru.matveev.alexey.main.helpers.UtilHelper.getRandomElement(UtilHelper.groovy:25) at ru.matveev.alexey.main.helpers.UtilHelper$getRandomElement.call(Unknown Source) at ru.matveev.alexey.main.postfunctions.pfNPE.run(pfNPE.groovy:12)
      
      





私たちが理解したログから。 呼び出されたスクリプトとその場所。 すべての変数の値が表示されます。 スクリプトを開いて、何が問題なのかを理解することができます。 私たちが持っているすべての情報。



事件3



ここで、インラインスクリプトのロギングを追加する必要があります。その後、スクリプトの呼び出し元とコンテキストを理解することもできます。



事件4



いくつかのオプションがあります。 たとえば、エグゼキュータが指定されたときに実行されるスクリプトを確実に知ることができます。 次に、スクリプトロガーのDEBUGレベルをオンにして、ログを調べます。 スクリプトがわからない場合は、すべてのスクリプト(ru.matveev.alexey.main)または特定のタイプのスクリプト(ru.matveev.alexey.main.listeners-リスナーのみ)に対してDEBUGレベルを設定できます。 さらに、ログの表示に使用するプログラムのロガーによってログの出力をフィルタリングし、必要なログに関する情報のみを受け取ることができます。 その後、アーティストを任命し、ログで何が起こるかを観察します。 スクリプトにログインしている場合、必要なすべての情報を取得します。



この記事で伝えたいことはこれだけです。 スクリプトのすべての行を誓約したいという願望があるかもしれないことを付け加えたいと思います。 スクリプトを理解するのは難しいため、これを行う必要はありません。 コードの読みやすさはロギングと同じくらい重要であることに留意してください。



All Articles