001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.system; 017 018import java.util.List; // 6.9.2.1 (2018/03/12) 019import java.util.ArrayList; // 6.9.2.1 (2018/03/12) 020import java.util.Set; // 6.9.2.1 (2018/03/12) 021import java.util.HashSet; // 6.9.2.1 (2018/03/12) 022import java.util.LinkedHashSet; // 7.0.6.4 (2019/11/29) 023import java.util.Iterator; // 7.0.6.4 (2019/11/29) 024 025import java.util.concurrent.ConcurrentMap; // 6.4.3.3 (2016/03/04) 026import java.util.concurrent.ConcurrentHashMap; 027import java.util.Collections; // 7.0.7.2 (2019/12/28) 028 029import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 030import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.4.2.0 (2016/01/29) 031 032/** 033 * ThrowUtil.java は、共通的に使用される Throwable,Exception関連メソッドを集約した、クラスです。 034 * 035 * StringUtil にあったメソッドを、こちらに移動させました。 036 * 037 * @og.group ユーティリティ 038 * 039 * @og.rev 6.4.2.0 (2016/01/29) 新規作成 040 * @og.rev 6.4.3.2 (2016/02/19) 全面書き換え 041 * 042 * @version 6.0 043 * @author Kazuhiko Hasegawa 044 * @since JDK8.0, 045 */ 046public final class ThrowUtil { 047 /** スタックトレースする行数 {@value} */ 048 public static final int MIN_STACK_SIZE = 3; // 先頭から、通常にスタックトレースする行数。 049 050 /** 6.9.2.1 (2018/03/12) 複数個所で呼ばれた場合の処理が、うまく出来ないので、暫定対策。(同時起動で、混ざる) */ 051 private static final CaseBuilder C_BUF = new CaseBuilder(); 052 053 /** 6.9.3.0 (2018/03/26) static で持ちます。 */ 054 private static final StackTraceElement NULL_STE = new StackTraceElement( "","","",-1 ); 055 056 /** 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 */ 057 private static final Set<Integer> CACHE_TH = new LinkedHashSet<>(); // 7.0.6.4 (2019/11/29) 058 059 /** 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 */ 060 private static final int MAX_CACHE_TH = 10; // 7.0.6.4 (2019/11/29) 061 062 /** 063 * デフォルトコンストラクターをprivateにして、 064 * オブジェクトの生成をさせないようにする。 065 */ 066 private ThrowUtil() {} 067 068 /** 069 * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。 070 * 071 * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動。 072 * @og.rev 6.4.2.0 (2016/01/29) すべてのスタックトレースを行っていたが、絞り込みます。 073 * 074 * @param th printStackTraceすべき元のThrowableオブジェクト 075 * 076 * @return Throwableの詳細メッセージ( th.printStackTrace() ) 077 * @og.rtnNotNull 078 */ 079 public static String ogStackTrace( final Throwable th ) { 080 return ogStackTrace( null , th ); 081 } 082 083 /** 084 * Throwable の printStackTrace() 結果の内、opengion に関する箇所だけを文字列に変換して返します。 085 * 086 * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" を 087 * 含む箇所だけを、抜粋します。 088 * また、同じ内容のエラーも除外します。 089 * 先頭から、MIN_STACK_SIZE 行は、元のままのメッセージを出力します。 090 * Throwable#getCause() で、再帰的に原因をさかのぼります。 091 * 092 * @og.rev 5.7.2.0 (2014/01/10) 新規作成 093 * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動し、msg 引数を追加。 094 * @og.rev 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。 095 * @og.rev 6.5.0.1 (2016/10/21) メッセージに、BuildNumber.ENGINE_INFO を含める。 096 * @og.rev 6.9.0.1 (2018/02/05) causeの対応が中途半端だったので、修正します。 097 * @og.rev 6.9.2.1 (2018/03/12) 最小件数のbreak判定の方法を見直します。 098 * @og.rev 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 099 * 100 * @param msg 合成したいメッセージ 101 * @param th 元のThrowableオブジェクト 102 * 103 * @return Throwableの詳細メッセージ( StackTraceElement の抜粋 ) 104 * @og.rtnNotNull 105 */ 106 public static String ogStackTrace( final String msg,final Throwable th ) { 107 C_BUF.init() 108 .append( "Version: " , BuildNumber.ENGINE_INFO ) // 6.5.0.1 (2016/10/21) エンジンのバージョン 109 .append( "Message: " , msg ); // 追加メッセージ 110 111// if( th != null ) { 112 if( isNewThr( th ) ) { // 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 113 // Throwable.toString() で、クラス名とメッセージを分けて、それぞれを重複チェックする。 114 C_BUF.append( "Error : " , th.getClass().getCanonicalName() ) // クラス名 115 .append( " " , th.getLocalizedMessage() ) // メッセージ 116 .addStackTrace( th , MIN_STACK_SIZE ); // 6.9.2.1 (2018/03/12) 117 118 // 原因の Throwable 119 Throwable tmpTh = th.getCause(); 120 while( tmpTh != null ) { 121 C_BUF.append( "Cause : " , tmpTh.getClass().getCanonicalName() ) // クラス名 122 .append( " " , tmpTh.getLocalizedMessage() ) // メッセージ 123 .addStackTrace( tmpTh , MIN_STACK_SIZE ); // 6.9.2.1 (2018/03/12) 124 125 tmpTh = tmpTh.getCause(); 126 } 127 128 // 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。 129 // このThrowable は、原因の Throwable は、求めません。 130 for( final Throwable supTh : th.getSuppressed() ) { 131 C_BUF.append( "Suppressed : " , supTh.getClass().getCanonicalName() ) // クラス名 132 .append( " " , supTh.getLocalizedMessage() ) // メッセージ 133 .addStackTrace( supTh , MIN_STACK_SIZE ); // 6.9.2.1 (2018/03/12) 134 } 135 } 136 137 return C_BUF.toString().trim(); 138 } 139 140 /** 141 * 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列を返します。 142 * 143 * 通常、System.out.println() で済ましていたエラーメッセージに対して、 144 * エラー発生場所を特定する為の情報を付与したメッセージを作成します。 145 * 発生場所の行番号は、このメソッド(実施は、オーバーロード先)で new Throwable して、取得します。 146 * 呼ぶ場所で、new Throwable() する代わりの簡易目祖度になります。 147 * 148 * @og.rev 6.4.2.0 (2016/01/29) 新規作成。 149 * 150 * @param msg 合成したいメッセージ 151 * 152 * @return 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列 153 * @og.rtnNotNull 154 */ 155 public static String ogThrowMsg( final String msg ) { 156 return ogThrowMsg( msg , null ); 157 } 158 159 /** 160 * 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列を返します。 161 * 162 * 通常、System.out.println() で済ましていたエラーメッセージに対して、 163 * エラー発生場所を特定する為の情報を付与したメッセージを作成します。 164 * 行番号は、引数のThrowableの最初の情報のみ使用する為、通常は、このメソッドを 165 * 呼ぶ場所で、new Throwable() します。 166 * 発生クラスは、org.opengion か、org.apache.jsp.jsp を含む3行のみの簡易メッセージとします。 167 * 168 * @og.rev 6.3.6.1 (2015/08/28) 新規作成 169 * @og.rev 6.3.6.1 (2015/08/28) メッセージに、th.getLocalizedMessage() を含める。 170 * @og.rev 6.3.9.0 (2015/11/06) thのnullチェックを先に行う。 171 * @og.rev 6.4.2.0 (2016/01/29) StringUtil にあったメソッドを移動するとともに、メソッド名を、ogErrMsg → ogThrowMsg に変更。 172 * @og.rev 6.5.0.1 (2016/10/21) メッセージに、BuildNumber.ENGINE_INFO を含める。 173 * @og.rev 6.9.0.1 (2018/02/05) causeの対応が中途半端だったので、修正します。 174 * @og.rev 6.9.2.1 (2018/03/12) 最小件数のbreak判定の方法を見直します。 175 * @og.rev 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 176 * 177 * @param msg 合成したいメッセージ 178 * @param th 元のThrowableオブジェクト 179 * 180 * @return 発生元を示すクラス、メソッド、行番号とともに、メッセージを合成した文字列 181 * @og.rtnNotNull 182 */ 183 public static String ogThrowMsg( final String msg,final Throwable th ) { 184 C_BUF.init() 185 .append( "Version: " , BuildNumber.ENGINE_INFO ) // 6.5.0.1 (2016/10/21) エンジンのバージョン 186 .append( "Message: " , msg ); // 追加メッセージ 187// if( th != null ) { 188 if( isNewThr( th ) ) { // 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 189 C_BUF.append( "Error : " , th.getClass().getCanonicalName() ) // クラス名 190 .append( " " , th.getLocalizedMessage() ); // メッセージ 191 192 // 原因の Throwable 193 Throwable tmpTh = th.getCause(); 194 while( tmpTh != null ) { 195 C_BUF.append( "Cause : " , tmpTh.getClass().getCanonicalName() ) // クラス名 196 .append( " " , tmpTh.getLocalizedMessage() ); // メッセージ 197 198 tmpTh = tmpTh.getCause(); 199 } 200 201 // 6.4.3.2 (2016/02/19) (通常try-with-resources文によって)抑制された例外も出力します。 202 // このThrowable は、原因の Throwable は、求めません。 203 for( final Throwable supTh : th.getSuppressed() ) { 204 C_BUF.append( "Suppressed : " , supTh.getClass().getCanonicalName() ) // クラス名 205 .append( " " , supTh.getLocalizedMessage() ); // メッセージ 206 } 207 } 208 209 return C_BUF.toThrowMsg().trim(); 210 } 211 212 /** 213 * Throwable の getStackTrace() 結果の内、opengion に関する箇所だけのStackTraceElement配列を選別して返します。 214 * 215 * 通常、スタックトレース情報は、膨大なメッセージが含まれるため、その中の、org.opengion か、org.apache.jsp.jsp を 216 * 含む箇所だけを、抜粋します。 217 * ただし、ThrowUtilクラス(このクラス)内で、スタックトレースを new しているため、このクラスで発生した 218 * スタックトレースは、含まないようにしています。このクラスからエラーが発生しないことを望みます。 219 * 処理は、先頭から、minCnt件数までは、そのまま残し、それ以降は、関連するトレース情報のみ残します。 220 * 221 * @og.rev 6.9.2.1 (2018/03/12) 選別だけを別に用意します。 222 * @og.rev 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 223 * 224 * @param th 元のThrowableオブジェクト(!= null 保障済み) 225 * @param minCnt StackTraceElementを登録する最小件数 226 * @return 選別されたStackTraceElement配列 227 * @og.rtnNotNull 228 * @see java.lang.Throwable#getStackTrace() 229 */ 230 public static StackTraceElement[] selectElement( final Throwable th , final int minCnt ) { 231 final List<StackTraceElement> list = new ArrayList<>(); 232 233 if( th != null ) { 234 int idx = 0; 235 final Set<String> cacheSet = new HashSet<>(); // 同一スタックの除外用 236 for( final StackTraceElement stEle : th.getStackTrace() ) { 237 final String stkMsg = stEle.toString(); 238 if( !cacheSet.add( stkMsg ) ) { continue; } // 同じメッセージを出さない対応 239 240 final String cls = stEle.getClassName(); 241 242 boolean flag = true; // 連続で、出力しない。 243 if( minCnt < 0 || idx < minCnt || cls.contains( "org.opengion" ) || cls.contains( "org.apache.jsp.jsp" ) ) { 244 list.add( stEle ); 245 flag = true; // 省略解除 246 } 247 else { 248 if( flag ) { 249 list.add( NULL_STE ); // StackTraceElementの省略 250 flag = false; // 連続で、出力しない。 251 } 252 } 253 idx++ ; 254 } 255 } 256 257// return list.toArray( new StackTraceElement[list.size()] ); 258 return list.toArray( new StackTraceElement[0] ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応 259 } 260 261 /** 262 * Throwable の重複チェックを行います。 263 * 264 * 新規の Throwable なら、true を、null か、重複している場合は、false を返します。 265 * MAX_CACHE_TH で、最大キャッシュ数を指定します。それ以上になった場合は、 266 * 最初に登録した Throwable を削除します。 267 * 268 * @og.rev 7.0.6.4 (2019/11/29) Throwable オブジェクトの重複を取り除きます。 269 * @og.rev 7.0.7.2 (2020/01/09) iterator()は、next() してからしかremove()できない。 270 * 271 * @param th printStackTraceすべき元のThrowableオブジェクト 272 * 273 * @return 新規なら true , 重複か null なら、false 274 * @og.rtnNotNull 275 */ 276 private static boolean isNewThr( final Throwable th ) { 277 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 278 boolean flag = false; 279 280 synchronized( CACHE_TH ) { 281 // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing 282// if( th != null && CACHE_TH.add( Integer.valueOf( th.hashCode() ) ) ) { 283 if( th != null && CACHE_TH.add( th.hashCode() ) ) { 284 // 制限数を超えた場合に、最初のオブジェクトを削除します。 285 if( CACHE_TH.size() > MAX_CACHE_TH ) { 286 final Iterator<Integer> ite = CACHE_TH.iterator(); 287 ite.next(); // 7.0.7.2 (2020/01/09) 288 ite.remove(); 289 } 290// return true; 291 flag = true; 292 } 293 } 294// return false; 295 return flag; 296 } 297 298 /** 299 * StringBuilder を、例外処理のスタックに特化した形で作り直した内部クラスです。 300 * 301 * printStackTrace() すると、膨大なメッセージが表示されるため、その中の、"org.opengion" と 302 * "org.apache.jsp.jsp" を含む箇所だけを、抜粋します。 303 * また、同じ内容のエラーも除外します。 304 * 305 * ※ 怪しい実装 306 * StackTrace は、内部的に、色々な箇所で呼ばれたり、同じ要因で、何度も再作成されたりします。 307 * この内部クラスも、ひとつの例外で、何度も作成されるため、重複チェックの方法を、時間制限で、 308 * 同一メッセージの重複処理を行っています。 309 * よって、まったく異なる要因のエラーが同時に発生した場合、重複処理の判定で、除外される可能性が 310 * あります。(発生場所の行番号が異なれば、重複処理されても残るので、デバッグ可能です) 311 * 312 * @og.rev 6.4.3.2 (2016/02/19) 新規追加 313 * @og.rev 6.9.2.1 (2018/03/12) 重複処理を、staticではなく、自身のオブジェクト内だけに変更します。 314 * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。 315 * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。 316 */ 317 private static final class CaseBuilder { 318 private static final String USE_KEY = "USE_KEY" ; 319 private static final long CACHE_TIME = 3000L; // Exceptionが発生した場合、連続してCallされるため、一まとめにする。 320 321 private final ConcurrentMap<String,String> MSG_MAP = new ConcurrentHashMap<>(); // 同期Setの代わり。重複の取り除き判定 322 private long lastCall ; // 初期値 0L ( 6.9.9.4 (2018/10/01) PDM: Use of modifier volatile is not recommended.) 323 324// private final List<String> list = new ArrayList<>(); 325 private final List<String> list = Collections.synchronizedList( new ArrayList<>() ); // 7.0.7.2 (2019/12/28) 326 327 /** 328 * デフォルトコンストラクタ。 329 * 330 * 時間制限が過ぎれば、キャッシュをクリアします。 331 * 332 * @og.rev 6.4.3.2 (2016/02/19) 新規追加 333 * @og.rev 6.4.3.3 (2016/03/04) synchronized を取り除きます。 334 * @og.rev 6.9.2.1 (2018/03/12) 重複処理を、staticではなく、自身のオブジェクト内だけに変更します。 335 * @og.rev 6.9.9.1 (2018/08/27) デフォルトコンストラクタに、super(); を呼び出すようにします。 336 */ 337 public CaseBuilder() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 338 339 /** 340 * 初期化します。 341 * 342 * @og.rev 6.9.2.1 (2018/03/12) 新規追加 343 * @og.rev 6.9.9.4 (2018/10/01) PDM: Use of modifier volatile is not recommended. 344 * 345 * @return 自分自身 346 * @og.rtnNotNull 347 */ 348 public CaseBuilder init() { 349// synchronized( MSG_MAP ) { 350 final long now = System.currentTimeMillis(); 351 if( now - lastCall > CACHE_TIME ) { // 前回キャッシュ時からの経過時刻が、CACHE_TIME を上回っている場合。 352 lastCall = now; 353 MSG_MAP.clear(); 354 list.clear(); 355 } 356// } 357 return this; 358 } 359 360 /** 361 * タイトルとメッセージを内部のStringBuilderに追記していきます。 362 * 363 * メッセージが、null や、空文字の場合は、何もしません。また、同一メッセージの追加は出来ません。 364 * 戻り値に、自分自身のオブジェクトを返すので、StringBuilder と同様に、接続できます。 365 * 366 * @og.rev 6.4.3.2 (2016/02/19) 新規追加 367 * @og.rev 6.4.3.3 (2016/03/04) 同一メッセージの判定を、trim() したキーで行います。 368 * @og.rev 6.9.2.1 (2018/03/12) タイトルがnullや空文字の場合も、なにもしません。 369 * 370 * @param title タイトルに相当します。 371 * @param msg メッセージ 372 * @return 自分自身 373 * @og.rtnNotNull 374 */ 375 public CaseBuilder append( final String title , final String msg ) { 376 if( title != null && !title.isEmpty() && msg != null && !msg.isEmpty() ) { 377 // 超特殊処理1 378 // msg に改行コードを含む場合、最初の改行で、前後に分けて、それぞれをキャッシュ判定します。 379 // SQL文など、行が多く出る場合に、何度も繰り返されるのを避けるためです。 380 // 通常、1行目は、Exception発生元のため、異なるケースがありますが、2行目以降は、 381 // 同じケースが多いための処置です。 382 final String msg0 = msg.trim(); 383 384 // Map#putIfAbsent : 戻り値は、以前の値。追加有り、置換なし(先勝)、削除なし 385 // Map#put と何が違うかというと、置き換えが無い。速度は誤差範囲です。 386 if( MSG_MAP.putIfAbsent( msg0,USE_KEY ) == null ) { // 同期Setの代わり。未登録時は、null が戻る。 387 final int adrs = msg0.indexOf( '\n' ); // よくない判定方法 388 if( adrs < 0 ) { 389 list.add( title + msg0 ); 390 } 391 else { 392 final String msg1 = msg0.substring( 0,adrs ).trim(); // 最初の改行で、前後に分割します。 393 final String msg2 = msg0.substring( adrs+1 ).trim(); 394 if( MSG_MAP.putIfAbsent( msg1,USE_KEY ) == null ) { 395 list.add( title + msg1 ); 396 } 397 if( MSG_MAP.putIfAbsent( msg2,USE_KEY ) == null ) { 398 list.add( title + msg2 ); 399 } 400 } 401 } 402 } 403 return this; 404 } 405 406 /** 407 * Throwable の getStackTrace() 結果の内、opengion に関する箇所だけのStackTraceElement配列をCaseBuilderオブジェクトに書き込みます。 408 * 409 * 通常、スタックトレース情報は、膨大なメッセージが含まれるため、その中の、org.opengion か、org.apache.jsp.jsp を 410 * 含む箇所だけを、抜粋します。 411 * ただし、ThrowUtilクラス(このクラス)内で、スタックトレースを new しているため、このクラスで発生した 412 * スタックトレースは、含まないようにしています。このクラスからエラーが発生しないことを望みます。 413 * 処理は、先頭から、MIN_STACK_SIZE 行は、元のままのメッセージを出力し、minCnt件数で、打ち切ります。 414 * minCnt が、0 か、マイナスの場合は、打ち切り制限なしです。( Integer.MAX_VALUE を指定させるのが嫌だっただけです。 ) 415 * 416 * @og.rev 6.4.2.0 (2016/01/29) 新規作成 417 * @og.rev 6.9.2.1 (2018/03/12) 選別だけを別に用意します。 418 * 419 * @param th 元のThrowableオブジェクト(!= null 保障済み) 420 * @param minCnt StackTraceElementを登録する最小件数 421 * @return 自分自身 422 * @og.rtnNotNull 423 * @see java.lang.Throwable#getStackTrace() 424 */ 425 public CaseBuilder addStackTrace( final Throwable th , final int minCnt ) { 426// for( final StackTraceElement stEle : ThrowUtil.selectElement( th,minCnt ) ) { 427 for( final StackTraceElement stEle : selectElement( th,minCnt ) ) { // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 428 if( stEle == null || "".equals( stEle.getClassName() ) ) { 429 append( " at " , "...." ); // StackTraceElement が省略されている。 430 } 431 else { 432 append( " at " , stEle.toString() ); 433 } 434 } 435 return this; 436 } 437 438 /** 439 * 内部のStringBuilderを、文字列に変換して返します。 440 * 441 * 出力の直前に改行コードを出しています。 442 * 443 * @og.rev 6.4.3.2 (2016/02/19) 新規追加 444 * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。 445 * @og.rev 7.0.5.0 (2019/09/09) StringBuilder#append を、String.join に変更。 446 * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。 447 * 448 * @return 内部のStringBuilderの文字列化されたもの 449 * @og.rtnNotNull 450 */ 451 @Override // Object 452 public String toString() { 453// synchronized( list ) { 454 return CR + String.join( CR,list ); 455// } 456 } 457 458 /** 459 * 内部のStringBuilderを、文字列に変換して返します。 460 * 461 * 出力の直前に改行コードを出しています。 462 * 463 * @og.rev 6.4.3.2 (2016/02/19) 新規追加 464 * @og.rev 6.9.9.1 (2018/08/27) StringBuilder を、List に変更したが、empty時の処理漏れ対応。 465 * @og.rev 7.0.5.0 (2019/09/09) synchronized しておきます。 466 * @og.rev 7.0.7.2 (2019/12/28) ArrayList を、Collections.synchronizedList に変更します。 467 * 468 * @return 内部のStringBuilderの文字列化されたもの 469 * @og.rtnNotNull 470 */ 471 public String toThrowMsg() { 472 if( list.isEmpty() ) { return CR; } // 6.9.9.1 (2018/08/27) 473 474// synchronized( list ) { 475 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 476 for( final String msg : list ) { 477 if( ! msg.startsWith( " at " ) ) { 478 buf.append( CR ).append( msg ); 479 } 480 } 481 return buf.toString(); 482// } 483 } 484 } 485}