コンテキスト検索 では、特定のマシン上のファイルなど、ログソースを指定し、特定のログエントリの前後にあるログを取得してそのコンテキストを確認できます。大量のログを処理する場合、PackId を付与することで関連するログエントリをグループ化できます。PackId を活用すると、関連するロググループ全体を迅速にクエリし、効率的にログコンテキストを特定できます。本トピックでは、ログに PackId を付与する方法について説明します。
仕組み
サーバーは PackId メカニズムを使用してログコンテキストを関連付けます。 PackId のフォーマットは コンテキストプレフィックス-ロググループ ID です。たとえば、5FA51423DDB54FDA-1E3 のようになります。 詳細は次のとおりです。
-
コンテキストプレフィックス:大文字の 16 進数で構成されます(例:
5FA51423DDB54FDA)。同じコンテキストプレフィックスを持つログは、同一のログコンテキストに属します。 -
ロググループ ID:大文字の 16 進数で構成されます(例:
1E3)。同一のログコンテキスト内では、ロググループ IDが増分となります(例:1E3および1E4は、同一ログコンテキスト内の隣接するロググループを表します)。
クライアントが PackId を生成し、ログ書き込みリクエストの一部としてサーバーに送信します。同じコンテキストプレフィックスを持つログは、同一のログコンテキストに属すると見なされます。
PackId の自動生成
-
Producer SDK を使用して書き込まれたログ:同一 Producer インスタンスから送信されたログデータは同一コンテキストに属し、そのままコンテキスト検索に利用できます。たとえば、Aliyun Log Java Producer や C Producer SDK を使用してログを書き込む場合、システムが自動的にコンテキスト識別子として
PackIdを生成・付与します。 -
Logtail を使用して収集されたログ:Logtail により収集されたログには、自動的に
PackIdが付与されます。ホストまたは Pod などの同一収集ソース上の同一ファイルから取得されたログは同一コンテキストに属し、そのままコンテキスト検索に利用できます。
PackId の手動生成と PutLogs を使用したアップロード
パラメーター
ログの書き込み API を使用して Simple Log Service へログを書き込みます。PackId を手動で生成した後は、LogGroup の LogTags プロパティ内に PackId を配置する必要があります。このとき、PutLogs リクエスト 内の Key は __pack_id__ に設定します。以下にパラメーターの例を示します:
{
"Topic": "my-topic",
"Source": "127.0.0.1",
"LogTags": [
{
"Key": "__pack_id__",
"Value": "5FA51423DDB54FDA-1"
},
{
"Key": "my_other_tag_key",
"Value": "my_other_tag_value"
}
],
"Logs": [
{
"Time": 1728961415,
"Contents": [
{
"Key": "hello",
"Value": "world"
}
]
}
]
}
コード例
Java の例
-
pom.xml ファイルに以下の依存関係を追加します。
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> <dependency> <groupId>com.aliyun.openservices</groupId> <artifactId>aliyun-log</artifactId> <version>0.6.111</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.12</version> </dependency> -
以下のコードを使用して PackId を手動で生成し、PutLogs API を呼び出してアップロードします。
project、logstore、endpoint、accessKeyId、およびaccessKeySecretの各パラメーターを実際の値に置き換えてください。package org.example; import com.aliyun.openservices.log.Client; import com.aliyun.openservices.log.common.LogItem; import com.aliyun.openservices.log.common.TagContent; import com.aliyun.openservices.log.exception.LogException; import com.aliyun.openservices.log.request.PutLogsRequest; import com.google.common.base.Charsets; import com.google.common.hash.Hashing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.concurrent.atomic.AtomicLong; public class Main { private static final String TAG_PACK_ID = "__pack_id__"; private static final int TOKEN_LEN = 4; public static void main(String[] args) throws LogException { System.out.println("Hello world!"); // 同一コンテキストでは同一の PackIdGenerator インスタンスを使用します。 PackIdGenerator generator1 = new PackIdGenerator(); System.out.println(generator1.generateNewPackId()); System.out.println(generator1.generateNewPackId()); System.out.println(generator1.generateNewPackId()); // 異なるコンテキストでは異なる PackIdGenerator インスタンスを使用します。 PackIdGenerator generator2 = new PackIdGenerator(); System.out.println(generator2.generateNewPackId()); System.out.println(generator2.generateNewPackId()); // ログ設定を構成します。 String project = "project"; String logstore = "logstore"; String topic = "topic"; String source = "127.0.0.1"; Client client = new Client("endpoint", "accessKeyId", "accessKeySecret"); List<LogItem> logs = new ArrayList<>(); LogItem log = new LogItem(); log.PushBack("hello", "world"); logs.add(log); // ログリクエストを送信します。 PutLogsRequest req = new PutLogsRequest(project, logstore, topic, source, logs); // タグリストに PackId を追加します。 req.SetTags(Arrays.asList(new TagContent(TAG_PACK_ID, generator1.generateNewPackId()))); client.PutLogs(req); // 別のログリクエストを送信します。 PutLogsRequest req2 = new PutLogsRequest(project, logstore, topic, source, logs); req.SetTags(Arrays.asList(new TagContent(TAG_PACK_ID, generator1.generateNewPackId()))); client.PutLogs(req2); } public static class NetworkUtils { private NetworkUtils() { } public static boolean isIpAddress(final String ipAddress) { if (ipAddress == null || ipAddress.isEmpty()) { return false; } try { final String[] tokens = ipAddress.split("\\."); if (tokens.length != TOKEN_LEN) { return false; } for (String token : tokens) { int i = Integer.parseInt(token); if (i < 0 || i > 255) { return false; } } return true; } catch (Exception ex) { return false; } } public static String getLocalMachineIp() { try { Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface ni = networkInterfaces.nextElement(); if (!ni.isUp()) { continue; } Enumeration<InetAddress> addresses = ni.getInetAddresses(); while (addresses.hasMoreElements()) { final InetAddress address = addresses.nextElement(); if (!address.isLinkLocalAddress() && address.getHostAddress() != null) { String ipAddress = address.getHostAddress(); if ("127.0.0.1".equals(ipAddress)) { continue; } if (isIpAddress(ipAddress)) { return ipAddress; } } } } } catch (SocketException ex) { // 無視します } return null; } } public static class PackIdGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(PackIdGenerator.class); private static final AtomicLong GENERATORID = new AtomicLong(0); private final String packIdPrefix; private final AtomicLong batchId = new AtomicLong(0); public PackIdGenerator() { packIdPrefix = generatePackIdPrefix(GENERATORID.getAndIncrement()).toUpperCase() + "-"; } public String generateNewPackId() { return packIdPrefix + Long.toHexString(batchId.getAndIncrement()).toUpperCase(); } private String generatePackIdPrefix(Long instanceId) { String ip = NetworkUtils.getLocalMachineIp(); if (ip == null) { LOGGER.warn("ローカルマシンの IP アドレスを取得できませんでした。IP を 127.0.0.1 に設定します"); ip = "127.0.0.1"; } String name = ManagementFactory.getRuntimeMXBean().getName(); String input = ip + "-" + name + "-" + instanceId; return Hashing.farmHashFingerprint64().hashString(input, Charsets.US_ASCII).toString(); } } }
Go の例
-
以下のコマンドを実行して Go SDK および protobuf 依存関係パッケージをインストールします。
go get -u github.com/aliyun/aliyun-log-go-sdk go get google.golang.org/protobuf -
以下のコードを使用して PackId を手動で生成し、PutLogs API を使用してアップロードします。
project、logstore、endpoint、accessKeyId、およびaccessKeySecretの各パラメーターを実際の値に置き換えてください。package main import ( "crypto/md5" "fmt" "os" "sync/atomic" "time" sls "github.com/aliyun/aliyun-log-go-sdk" "google.golang.org/protobuf/proto" ) func main() { // 同一コンテキストでは同一の PackIdGenerator インスタンスを使用します。 g1 := NewPackIdGenerator() fmt.Println(g1.Generate()) fmt.Println(g1.Generate()) fmt.Println(g1.Generate()) // 異なるコンテキストでは異なる PackIdGenerator インスタンスを使用します。 g2 := NewPackIdGenerator() fmt.Println(g2.Generate()) fmt.Println(g2.Generate()) // ログ設定を構成します。 project := "project" logstore := "logStore" topic := "topic" source := "source" client := sls.CreateNormalInterface("endpoint", "accessKeyId", "accessKeySecret", "") logs := []*sls.Log{ { Time: proto.Uint32(uint32(time.Now().Unix())), Contents: []*sls.LogContent{ { Key: proto.String("hello"), Value: proto.String("world"), }, { Key: proto.String("hi"), Value: proto.String("world"), }, }, }, } // PostLogStoreLogsV2 API を使用してログを書き込みます。 err := client.PostLogStoreLogsV2(project, logstore, &sls.PostLogStoreLogsRequest{ LogGroup: &sls.LogGroup{ Topic: &topic, Source: &source, Logs: logs, LogTags: []*sls.LogTag{ { Key: proto.String("__pack_id__"), // タグリストに PackId を追加します。 Value: proto.String(g1.Generate()), }, }, }, }) if err != nil { panic(err) } // PutLogs API を使用してログを書き込みます。g1.Generate() で新しい PackId を作成します。 err = client.PutLogs(project, logstore, &sls.LogGroup{ Topic: &topic, Source: &source, Logs: logs, LogTags: []*sls.LogTag{ { Key: proto.String("__pack_id__"), // タグリストに PackId を追加します。 Value: proto.String(g1.Generate()), }, }, }) if err != nil { panic(err) } } type PackIdGenerator struct { prefix string id atomic.Uint64 } func NewPackIdGenerator() *PackIdGenerator { return &PackIdGenerator{ prefix: generatePackIDPrefix(), id: atomic.Uint64{}, } } func (g *PackIdGenerator) Generate() string { return fmt.Sprintf("%s-%X", g.prefix, g.id.Add(1)) } // ホスト名、PID、時刻に基づくコンテキストを作成します。 func generatePackIDPrefix() string { m := md5.New() m.Write([]byte(time.Now().String())) hostName, _ := os.Hostname() m.Write([]byte(hostName)) m.Write([]byte(fmt.Sprintf("%v", os.Getpid()))) return fmt.Sprintf("%X", m.Sum(nil)) }
ログコンテキストの表示
-
プロジェクト内で、送信先ログストアをクリックします。 タブで、対象のログエントリを検索し、
アイコンをクリックします。説明PutLogs API を使用して送信されたログの各バッチにおいて、同一の
PackIdコンテキストプレフィックスを持つログは同一のログコンテキストに属します。Simple Log Service はPackIdを基にログを自動的にグループ化します。コンテキスト表示 をクリックすると、特定のログのコンテキストが表示されます。これにより、同一のコンテキストプレフィックスを持つロググループが表示され、対象のログエントリがハイライト表示されます。
-
ページを上下にスクロールしてログコンテキストを表示します。

参考文献
-
コンソール上で特定のログのコンテキストを表示する方法については、「コンテキスト検索」をご参照ください。
-
SDK または API を使用して Simple Log Service へログデータを書き込む方法については、「Aliyun Log Java Producer を使用したログデータの書き込み」、「C Producer SDK」、または「PutLogs」をご参照ください。