序文
みなさん、こんにちは。カタツムリを拾う男の子です。ログは、問題をすばやく特定するための優れたヘルパーであり、ポットを引き裂いて投げるための強力なツールです。ログをうまく印刷することは非常に重要です。今日は、ログ印刷のための15の良い提案について話しましょう〜
- 公開番号:カタツムリを拾う男の子
1.適切なログレベルを選択します
5つの一般的なログレベル、つまりエラー、警告、情報、デバッグ、およびトレースがあります。日々の開発では、情報をバックハンドで印刷するだけでなく、適切なログレベルを選択する必要があります〜
- エラー:エラーログ。通常のビジネスに影響を及ぼし、運用および保守の構成の監視が必要な比較的深刻なエラーを指します。
- 警告:警告ログ、一般的なエラーは、ビジネスにほとんど影響を与えませんが、開発の注意が必要です。
- info:情報ログ。呼び出し時間、入力および出力パラメーターなど、トラブルシューティングのための重要な情報を記録します。
- デバッグ:DEBUGを開発するための主要なロジックの実行時データ。
- トレース:最も詳細な情報。通常、この情報はログファイルにのみ記録されます。
2.ログは、メソッドの入力パラメーターと出力パラメーターを出力する必要があります
多くのログを印刷する必要はありません。問題をすばやく特定できる有効なログを印刷するだけです。効果的なログは、ポットを捨てるための強力なツールです!
有効で重要なログは何ですか?たとえば、メソッドが入ってくると、入力パラメータを出力します。次に、メソッドが戻ると、パラメーターを出力して値を返します。入力パラメーターの場合、これは通常、userIdやbizSeqなどの重要な情報です。例えば:
public String testLogMethod(Document doc, Mode mode){
log.debug(“method enter param:{}”,userId);
String id = "666";
log.debug(“method exit param:{}”,id);
return id;
}
复制代码
3.適切なログ形式を選択します
理想的なログ形式には、現在のタイムスタンプ(通常はミリ秒の精度)、ログレベル、スレッド名などの最も基本的な情報が含まれている必要があります。ログバックログは次のように構成できます。
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%thread][%logger{0}] %m%n</pattern>
</encoder>
</appender>
复制代码
ログ形式に現在の時刻が記録されていない場合は、リクエストの時刻すらわかりません。
4. if ... else ...などの条件が発生した場合は、各ブランチの最初の行にログを印刷してみてください
if ... else ...やswitchなどの状況が発生した場合は、ブランチの最初の行にログを出力できるため、問題のトラブルシューティング時に、ログを使用して入力したブランチを判別できます。コードロジックがより明確になり、問題のトラブルシューティングも容易になります。
正例:
if(user.isVip()){
log.info("该用户是会员,Id:{},开始处理会员逻辑",user,getUserId());
//会员逻辑
}else{
log.info("该用户是非会员,Id:{},开始处理非会员逻辑",user,getUserId())
//非会员逻辑
}
复制代码
5.ログレベルが比較的低い場合は、ログ切り替え判定を行ってください。
トレース/デバッグなどのより低いログレベルの場合、ログレベルの切り替え判断を実行する必要があります。
正例:
User user = new User(666L, "公众号", "捡田螺的小男孩");
if (log.isDebugEnabled()) {
log.debug("userId is: {}", user.getId());
}
复制代码
現在、次のログコードがあるためです。
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
复制代码
設定されたログレベルが警告された場合、上記のログは出力されませんが、文字列連結操作が実行されます。symbol
オブジェクトの場合、toString()
メソッドも実行され、システムリソースが浪費されます。上記の操作が実行された後、最終ログは印刷されないため、ログスイッチ判定を追加することをお勧めします。
6.ロギングシステム(Log4j、Logback)のAPIは直接使用できませんが、ロギングフレームワークSLF4JのAPIを使用できます。
SLF4Jは、ファサードモードのロギングフレームワークであり、さまざまなクラスのメンテナンスメソッドとログ処理メソッドの統合に役立ち、コードを変更することなく、基盤となるロギングフレームワークを簡単に置き換えることができます。
正例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(TianLuoBoy.class)
;
7.+スプライシングの代わりにパラメータプレースホルダー{}を使用することをお勧めします。
反例:
logger.info("Processing trade with id: " + id + " and symbol: " + symbol);
复制代码
上記の例では、+
演算子を使用して文字列を連結すると、パフォーマンスがある程度低下します。
例えば:
logger.info("Processing trade with id: {} and symbol : {} ", id, symbol);
复制代码
{}
ログのプレースホルダーとして中括弧を使用します。これは+
、演算子を使用するよりもエレガントで簡潔です。また、否定的な例と比較すると、プレースホルダーの使用は単なる置換アクションであり、パフォーマンスを効果的に向上させることができます。
8.非同期でログを出力することをお勧めします。
- ログは最終的にファイルまたは他の出力ストリームに出力され、IOパフォーマンスが必要になります。非同期の場合、IOパフォーマンスを大幅に向上させることができます。
- 特別な要件がない限り、ログを出力するには非同期の方法を使用することをお勧めします。ログバックを例にとると、非同期を構成するのは非常に簡単です。AsyncAppenderを使用するだけです。
<appender name="FILE_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="ASYNC"/>
</appender>
复制代码
9. e.printStackTrace()を使用しないでください
反例:
try{
// 业务代码处理
}catch(Exception e){
e.printStackTrace();
}
复制代码
正例:
try{
// 业务代码处理
}catch(Exception e){
log.error("你的程序有异常啦",e);
}
复制代码
理由:
- e.printStackTrace()によって出力されるスタックログは、ビジネスコードログとインターリーブされており、通常、異常なログをチェックするのは便利ではありません。
- e.printStackTrace()ステートメントによって生成された文字列は、スタック情報を記録します。情報が長すぎる場合、文字列定数プールが配置されているメモリブロックにスペースがない、つまりメモリがいっぱいである場合、ユーザーの要求は次のようになります。立ち往生〜
10.例外ログの半分を入力するだけでなく、すべてのエラーメッセージを出力します
反例1:
try {
//业务代码处理
} catch (Exception e) {
// 错误
LOG.error('你的程序有异常啦');
}
コピーコード
- 例外eはいずれも出力されないため、どのタイプの例外がスローされたかはわかりません。
反例2:
try {
//业务代码处理
} catch (Exception e) {
// 错误
LOG.error('你的程序有异常啦', e.getMessage());
}
复制代码
e.getMessage()
詳細なスタック例外情報は記録されず、基本的なエラーの説明情報のみが記録されます。これはトラブルシューティングに役立ちません。
正例:
try {
//业务代码处理
} catch (Exception e) {
// 错误
LOG.error('你的程序有异常啦', e);
}
复制代码
11.オンライン環境でデバッグを無効にする
オンライン環境でデバッグを無効にすることは非常に重要です。
一般的なシステムには多くのデバッグログがあり、さまざまなフレームワークでもデバッグログが多く使用されるため、オンラインデバッグによってディスクがすぐにいっぱいになり、ビジネスシステムの通常の動作に影響を与える可能性があります。
12.例外をログに記録して再度スローしないでください
反対は次のとおりです。
log.error("IO exception", e);
throw new MyException(e);
复制代码
- この実装は通常、スタック情報を2回出力します。これは、MyExceptionがキャッチされた場所で、再度出力されるためです。
- このようなロギング、またはラッピングしてからスローする場合は、両方を使用しないでください。そうしないと、ログがわかりにくくなります。
13.ログの繰り返し印刷を避ける
ログを繰り返し印刷することは避けてください。Jiangziはディスク領域を浪費します。意味を明確に表すログの行がすでにある場合は、冗長な印刷を避けてください。反例は次のとおりです。
if(user.isVip()){
log.info("该用户是会员,Id:{}",user,getUserId());
//冗余,可以跟前面的日志合并一起
log.info("开始处理会员逻辑,id:{}",user,getUserId());
//会员逻辑
}else{
//非会员逻辑
}
复制代码
log4jロギングフレームワークを使用している場合はlog4j.xml
、ログの繰り返し印刷を回避できるため、必ずadditivity=falseを設定してください。
正例:
<logger name="com.taobao.dubbo.config" additivity="false">
复制代码
14.ログファイルの分離
- access.logやエラーレベルerror.logなど、さまざまな種類のログを分離して、ファイルに個別に出力できます。
- もちろん、さまざまなビジネスモジュールに応じてさまざまなログファイルに印刷することもできるため、問題のトラブルシューティングやデータ統計の実行がより便利になります。
15.コア汎用モジュールの場合、より完全なログを印刷することをお奨めします
- 私たちの日常の開発では、コアまたはロジックが複雑なコードである場合、詳細なコメントとより詳細なログを追加することをお勧めします。
- ログはどの程度詳細にする必要がありますか?考えてみてください。コアプログラムにエラーがある場合は、ログから見つけることができます。