10 行のコードにより、ログ ストレージが 80% 削減されます。

序文

契約履行管理は物流業者向けの OMS ワークベンチです。初代が棚を設置して以来、追加投資はありません。その後、パートナーの学生が日々のメンテナンスと需要サポートを担当しています。数年間にわたる猛烈な成長を経て、システムは肥大化しすぎて混乱に満ちています。その後、それは未所有の土地になり、業界の共同建設によって支えられました。業界の孤立をサポートしないシステムの場合、業界の共同構築はシステムがすぐに衰退することを意味します。二年前、契約履行の管理を引き継ぎ、この広大な荒野にやって来た私も、創造の喜びを渇望し、たまたま手に鎌と槌を手にした人々と同じように、暴れ馬のように暴れまわっていました。思い当たることも多く、契約履行管理の完璧なシステムを構築したいという大胆で斬新なアイデアがたくさんありました。実際に実践できるアイデアがほとんどないのは残念です。この記事は、実践されている数少ないアイデアの 1 つであり、かなりの実用的価値があります。参考として必要な学生に編集され、共有されています。

ログカオス

ロギングは、日常の開発において最も無視され、悪用される可能性が最も高いモジュールです。ロギングは本当に単純なことなので無視されます。先人たちはログバックを設計しました。印刷できるかどうかはわかりませんが、どのファイルに印刷されるかはわかりません。とにかく、一度実行してみて、印刷されない場合は、作業して、エラーを変更します。さまざまなシナリオでログの形式と内容が大きく異なるため、またはログ記録方法が柔軟すぎ、カジュアルすぎ、スタイルが多様すぎるため、同意できない場合はほぼ全員が独自の LogUtil を作成する必要があるため、悪用されます。 「これは私がこれまで見た中で最も誇張されたものです。はい、システムへのログインに使用されるツールの種類は 20 から 30 もあります。将来の世代がどのツールを使用するか悩むのに 30 分かかるかもしれません。これは、何を説明するかを完全に説明しています」割れ窓効果です。学習するための最良の方法は、ネガティブな教材から学ぶことです。以下に、ログの設計および開発プロセスで最も一般的な問題のいくつかを示します。

分類カオス

一般に、システムでは、さまざまなビジネスやシナリオを区別するために複数のログ ファイルを設計する必要がありますが、すべてのログを 1 つのファイルに記録することは不可能です。しかし、誰も分類方法を教えてくれなかったので、さまざまな分類があります。システムモジュールによると。この分類は最も基本的な分類であり、最も階層的な分類となります。たとえば、フルフィルメント サービス センターのシステム階層化です。基本的に各レイヤーはログ ファイルに対応します。

テナント状況に応じて。一般に、ミドルエンド システムは複数のテナント (業界) をサポートし、各テナントは個別のログ ファイルに対応します。通常、テナントを完全に分離する場合を除き、この分類を単独で使用することはありません。意識の流れの分類。MECEルールに準拠しておらず、明確で統一された分類ロジックは存在せず、業務別、システムモジュール別、インターフェース能力別、新旧リンク別に分かれており、様々な分類方法の影が見え、結果は以下の通りである。数十の分類ファイルがあるため、ログを作成する人は、ログのこの行がどのファイルに入力されるのかわかりません。上記の分類方法はいずれも完全に純粋ではありません。どの分類方法であっても、最初に境界がどれほど明確に設計されていたとしても、時間が経つにつれて、最終的にはごちゃ混ぜに進化するからです。

  • 特定のクラスによって生成されたログを個別に監視し、新しいログ ファイルを追加したいと考えている人がいます。
  • 商品のパレットなどの新しいビジネスを追加し、それを個別に監視したい場合は、新しいログ ファイルを追加します。
  • サービスベースのキャンペーンを開始し、サービスベースのリンクを個別に監視し、新しいログ ファイルを追加しました。
  • 特定の企業がユーザーの行動を収集したいが、すべてのログ メッセージを受信したくない場合は、新しいログ ファイルを追加します。
  • 資産損失の危険にさらされるシナリオには特別な注意が必要であり、新しいログ ファイルが追加されます。
  • 大規模な販売、新しいログ ファイルなど、特別な期間に生成されたログ。

これらすべてでは十分ではありません。「いいえ」が見つかりました。人々が新しいログ ファイルを追加したいという衝動に駆られる瞬間は常にあります。彼らの要求やシナリオは不合理ではありません。これらのログのサイズはまったく無関係ですが、この衝動を止めることはできません。元の丸太のデザインは、さまざまな利害関係者によって絶えず引き剥がされ続ける瀕死の象のようなものでした。

フォーマットカオス

ログには特定の形式が必要であるという事実に異論を唱える人はいないでしょう。形式の混乱は主に 2 つの側面に反映されています。1 つは形式の設計です。一部のシステムは、非常に複雑な形式を設計しています。さまざまな区切り文字の組み合わせをサポートし、ログ コンテンツのグループ化をサポートし、キーワード ポジショニングを使用して固定位置形式を置き換え、人間の脳やコンピューターにとって解析の負担となる形式の拡張をサポートします。2 つ目は同じログ ファイルですが、異なる形式のコンテンツが表示される可能性があり、スタック ログと通常のビジネス ログが混在します。例を見てみましょう。ヒントは与えません。このログの構造を頭の中ですぐに分析できますか?

requestParam$&trace@2150435916867358634668899ebccf&scene@test&logTime@2023-06-14 17:44:23&+skuPromiseInfo$&itemId@1234567:1&skuId@8888:1&buyerId@777:1&itemTags@,123:1,2049:1,249:1,&sellerId@6294:1&toCode@371621:1&toTownCode@371621003:1&skuBizCode@TMALL_TAOBAO:1&skuSubBizCode@TMALL_DEFAULT:1&fromCode@DZ_001:1+orderCommonInfo$&orderId@4a04c79734652f6bd7a8876379399777&orderBizCode@TMALL_TAOBAO&orderSubBizCode@TMALL_DEFAULT&toCode@371621&toTownCode@371621003&+

道具の混乱

場合によっては、同じクラス、同じメソッドに 2 つの異なるログ行があり、ログの形式が異なり、ログ ファイルも異なることが起こります。なぜこのようなことが起こるのでしょうか? これは、さまざまなロギング ツールが使用されているためです。この原因を解明するには、さまざまなツールが何を行っているのかを正確に分析する必要があります。多くのツールの違いは、印刷注文オブジェクト、印刷メッセージ、印刷スケジュール ログなど、さまざまなパラメータ タイプをサポートしていることです。また、ワンパッケージ商品向けの専用ツールやマイナス売上向けの専用ツールなど、ビジネス シナリオごとにいくつかの違いがあります。また、異なる例外のカプセル化にもいくつかの違いがあり、一部は ExceptionA を出力し、一部は ExceptionB を出力します。この世にはこれほど奇妙なものはなく、おそらくそれは存在が合理的であるという事実によってのみ説明できるでしょう。

ログの階層化

私は常にミニマリスト デザインの原則を信じてきました。シンプルさは壊れないことを意味します。前述したように、ログ システムの最終的な結果はカオスになるはずですが、この傾向は避けられないため、初期設計時に保証できることは 1 つだけで、元の分類ができるだけ単純で重複しないようにすることです。実は一般的な分類方法は、機能ごとに横に分ける方法と、事業ごとに縦に分ける方法の2つしかありません。一般に、第 1 レベルの分類には水平分割を使用する必要があります。一般に業務の境界は明確に引くことが難しく、比較的曖昧であるため、機能の境界は比較的明確かつ安定しており、機能は実際にワークフローを反映しており、一度ワークフローが形成されると、基本的に構造的な変更はほとんどありません。この考えに基づいて、次のようなログ階層化を設計しました。

階層的な観点から見ると、実際には入口、コア、出口の 3 つの層しかありません。入口ログは、HSF やコントローラなどのトラフィック入口の受信パラメータと送信パラメータを出力することのみを担当します。終了ログは、すべてのサードパーティ サービス呼び出しの受信パラメータと送信パラメータを出力する役割を果たします。カーネル ログは、中間実行中にすべてのビジネス ログを出力します。たった 3 層で十分で、十分シンプルで、重かったり漏れたりすることはありません。また、スタック ログを分離します。スタックはビジネス ログに比べて非常に特殊です。この記事のタイトルにあるログ ストレージ削減の最適化は、スタック ログの最適化にすぎません。これについては後で説明します。

フォーマットデザイン

ログの形式設計にもいくつかの考慮事項があります。まず、言うまでもなく、ログのデザインは人間が判読できるものになっています。もう 1 つの非常に重要な点は、監視しやすさを考慮した設計ですが、これは多くの人が見落としがちな点です。この 2 つの原則に基づいて、フォーマット設計に関する私の考えをいくつかお話しします。まず次元の抽象化を行う必要があります。モニタリングを目的としているため、モニタリングでは通常、業界ディメンション、サービスディメンション、マーチャントディメンションなどの複数のメンテナンスをサポートする必要があるため、すべてのディメンション要素を抽出する必要があります。では、これらの寸法は、実際に印刷されるときにどのようにしてロガーに渡されるのでしょうか? これらを ThreadLocal に保存し、入力時にコンテキストから取得することをお勧めします。これを行うことのもう 1 つの利点は、ログ印刷ツールの設計が非常に洗練されており、渡す必要があるパラメーターはわずかだけであることです。形式は、構成よりも規約の原則を使用して、できるだけ単純にする必要があります。各次元は固定位置を占め、カンマで区切られます。大規模で包括的なモデルを設計してから、モデル全体を JSON 文字列に直接シリアル化しないでください。たとえこの機能を簡単に提供できたとしても、ユーザーが形式を簡単にカスタマイズできる、いわゆる拡張性に誘惑されないでください。私の経験では、この拡張性は必ず悪用され、最終的には設計者ですら実際の形式が何なのかわかりません。もちろん、デザイナーには高いビジョンと先見性が求められますが、難しいのはそこではなく、自分の技術を誇示したいという欲求をいかに抑えるかということです。内容に関しては、名前だけで理解できるように、一目瞭然のテキストを印刷するようにしてください。たとえば、返金ラベルを印刷したいとします。返金ラベルは、もともと 1、2、4、8 などのバイナリ ビットで格納されています。印刷するときは、格納されている値を直接印刷せず、英語のコードに変換してください。その意味を説明できます。フォーマット例

timeStamp|threadName logLevel loggerName|sourceAppName,flowId,traceId,sceneCode,identityCode,loginUserId,scpCode,rpcId,isYace,ip||businessCode,isSuccess||parameters||returnResult||

内容例

2023-08-14 14:37:12.919|http-nio-7001-exec-10 INFO c.a.u.m.s.a.LogAspect|default,c04e4b7ccc2a421995308b3b33503dda,0bb6d59616183822328322237e84cc,queryOrderStatus,XIAODIAN,5000000000014,123456,0.1.1.8,null,255.255.255.255||queryOrderStatus,success||{"@type":"com.alibaba.common.model.queryorder.req.QueryOrderListReq","currentUserDTO":{"bizGroup":888,"shopIdList":[123456],"supplierIdList":[1234,100000000001,100000000002,100000000004]},"extendFields":{"@type":"java.util.HashMap"},"invokeInfoDTO":{"appName":"uop-portal","operatorId":"1110","operatorName":"account_ANXRKY8NfqFjXvQ"},"orderQueryDTO":{"extendFields":{"@type":"java.util.HashMap"},"logisTypeList":[0,1],"pageSize":20,"pageStart":1},"routeRuleParam":{"@type":"java.util.HashMap","bizGroup":199000},"rule":{"$ref":"$.routeRuleParam"}}||{"@type":"com.alibaba.common.model.ResultDTO","idempotent":false,"needRetry":false,"result":{"@type":"com.alibaba.common.model.queryorderstatus.QueryOrderStatusResp","extendFields":{"@type":"java.util.HashMap"}},"success":true}||

スタックビートダウン

ここからが今回の本題ですが、このデザインが冒頭で述べた素晴らしいアイデアです。スタック反転は、別のシステム問題のトラブルシューティングのプロセス中に私が感じたいくつかの問題点から始まりました。まずスタックの例を見てみましょう。

これほど長いスタックとこれほど密集した文字は、毎日これを扱う開発者であっても、一目見ただけでは頭皮がしびれると思います。スタックを確認するときに本当に取得したい情報は何なのかを思い出してください。したがって、私が感じている核心的な問題点は 2 つあります。1 つ目は、SLS (Alibaba Cloud Log Product System) で見つかったログがデフォルトで折りたたまれていることです。スタックに関しては、従来の例外スタックの特徴は、最上位の例外がトラフィック エントリに最も近い例外であることであることを誰もが知っておく必要がありますが、通常、この種の例外についてはあまり気にしません。最も低レベルの例外は一連のエラーの原因であり、私たちが日常的に問題のトラブルシューティングを行う場合、多くの場合、エラーの原因が最も懸念されます。したがって、スタック ログの場合は、概要を見てもコードのどの行に問題があるのか​​一目では分からず、ソースを特定するには、ログをクリックして一番下までスクロールし、最後のスタックを確認する必要があります。問題を説明するためにバグの例を書きました。従来のスタック構造は実際には 2 つの部分に分かれており、これを例外原因スタックとエラースタックと呼んでいます。

上記のように、スタックには 3 つの例外グループが含まれており、それぞれの RuntimeException が例外であり、これら 3 つの例外がつながったとき、それを例外原因スタックと呼びます各 RuntimeException 内のスタックは、エラー スタックと呼ばれます。説明すると、これら 2 つの用語は私が作ったものですが、これらを区別している人は見たことがありません。通常、これらをスタックと呼んでいます。私の言いたいことを読者が理解できれば、名詞はあまり気にしなくて大丈夫です。2 番目の問題点は、この種のスタックのストレージ コストが高すぎることと、有効な情報伝達率が非常に低いことです。正直、現場の開発者にはあまり意識されていないかもしれませんが、コスト削減や効率化が求められる環境下では、一人ひとりがこれを自分のOKRとして実践し、受動的なものから能動的なものに変えていく必要があります。将来 会社は機械コストと人件費の間で多肢選択式の質問をしなければなりません。目標が明確になったので、適切な薬の処方を始めましょう。核となるアイデアは 2 つあります。スタックの折りたたみの問題を解決するには、スタック反転が使用されます。倒すと一番上に最低レベルの異常が配置されており、クリックしなくても一目で理由が分かります。

同時に、例外原因スタックの層数とエラースタックの層数の設定もサポートします。この問題の解決は、基本的に単純なアルゴリズムの問​​題です。つまり、スタックの最後の N 要素を逆順に出力します。コアコードは次のとおりです。

/**
 * 递归逆向打印堆栈及cause(即从最底层的异常开始往上打)
 * @param t 原始异常
 * @param causeDepth 需要递归打印的cause的最大深度
 * @param counter 当前打印的cause的深度计数器(这里必须用引用类型,如果用基本数据类型,你对计数器的修改只能对当前栈帧可见,但是这个计数器,又必须在所有栈帧中可见,所以只能用引用类型)
 * @param stackDepth 每一个异常栈的打印深度
 * @param sb 字符串构造器
 */
public static void recursiveReversePrintStackCause(Throwable t, int causeDepth, ForwardCounter counter, int stackDepth, StringBuilder sb){
    if(t == null){
        return;
    }
    if (t.getCause() != null){
        recursiveReversePrintStackCause(t.getCause(), causeDepth, counter, stackDepth, sb);
    }
    if(counter.i++ < causeDepth){
        doPrintStack(t, stackDepth, sb);
    }
}

ストレージ コストを削減し、情報が歪まないようにするには、スタック行から開始し、完全修飾クラス名を完全なクラス名に単純化することを検討します。パッケージ パスの最初の文字のみが入力され、行番号は保持されます。例:caumsLogAspect#log:88。コアコードは次のとおりです。

public static void doPrintStack(Throwable t, int stackDepth, StringBuilder sb){
    StackTraceElement[] stackTraceElements = t.getStackTrace();
    if(sb.lastIndexOf("\t") > -1){
        sb.deleteCharAt(sb.length()-1);
        sb.append("Caused: ");
    }
    sb.append(t.getClass().getName()).append(": ").append(t.getMessage()).append("\n\t");
    for(int i=0; i < stackDepth; ++i){
        if(i >= stackTraceElements.length){
            break;
        }
        StackTraceElement element = stackTraceElements[i];
        sb.append(reduceClassName(element.getClassName()))
          .append("#")
          .append(element.getMethodName())
          .append(":")
          .append(element.getLineNumber())
          .append("\n\t");
    }
}

最終的な効果はおそらくこんな感じになると思います。比較対象となるスタックをランダムに選択して文字数を計測したところ、同じ情報量で圧縮率は88%に達しました。

思考の拡張

多くの記事は、いわゆるベスト プラクティスを推奨することを好みますが、私の意見では、ベスト プラクティスは誤った命題です。ベスト プラクティスについて話すときは、「ベスト」が誰と比較されるのか、適用範囲が何なのかを明確にする必要があります。自分のフレームワークやソリューションが普遍的であるとあえて言う人はいないと思います。正確です。この記事で提案されているログ設計手法は、典型的なミドルエンド アプリケーションに実装されました。3 段階のログ階層化スキームは十分にシンプルで多用途ですが、最近、一部のリッチ クライアント アプリケーションで使用されています。このスキームは再配置する必要があります。ローカリゼーション変換が必要になる場合があります。それらの特徴は、サードパーティのサービスへの依存度が低く、多数のキャッシュ設計を使用することです。この設計の基礎となるロジックは、配布によって生じるリスクとコストを削減するために、すべてのロジックをローカル クライアント上で実行できるようにすることです。これは、ログの 99% が内部実行ロジックによって生成される可能性があることを意味するため、ログを他の次元から分割することを考慮する必要があります。また、ログ コストの削減に関して、この記事ではスタック ストレージの削減についてのみ説明していますが、システム内のすべてのログをスタックにすることは不可能であるため、実際の全体的なログ ストレージ コストはそれほど削減されない可能性があります。これだけ話しましたが、最終的には「特効薬や減量薬などについて迷信を持たないでください。すべてのテクノロジーやアイデアは、自分のニーズと能力に合わせて調整する必要があります。」という一文が残っています。

著者|傅南

クリックして今すぐクラウド製品を無料で試し、クラウドでの実践的な取り組みを始めましょう!

元のリンク

この記事は Alibaba Cloud のオリジナル コンテンツであり、許可なく複製することはできません。

200元の罰金と100万元以上を没収 You Yuxi: 高品質の中国語文書の重要性 MuskのJDK 21用ハードコア移行サーバー Solon、仮想スレッドは信じられないほど素晴らしい! TCP 輻輳制御によりインターネットが節約される OpenHarmony 用の Flutter が登場 Linux カーネルの LTS 期間が 6 年から 2 年に復元される Go 1.22 で for ループ変数エラーが修正される Svelte は「新しいホイール」を作成 - ルーン文字 Google が創立 25 周年を祝う
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/yunqi/blog/10112501