Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11722] ファイルの概要

このコミットは、Go言語の標準ライブラリである log/syslog パッケージにおける、Writer インターフェースの実装に関する修正です。具体的には、netConn 型の writeBytes および writeString メソッドが返す値が、実際にユーザーが提供したデータの長さを示すように変更されています。

コミット

commit 3fce00d99e30d66f63f8e3cb85debc137329db0d
Author: Rob Pike <r@golang.org>
Date:   Thu Feb 9 08:36:13 2012 +1100

    log/syslog: return length of data provided by the user, not length of header
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5644059
---
 src/pkg/log/syslog/syslog.go | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/pkg/log/syslog/syslog.go b/src/pkg/log/syslog/syslog.go
index aef63480f1..700b983c75 100644
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -136,11 +136,19 @@ func (w *Writer) Debug(m string) (err error) {
 }
 
 func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	if err != nil {
+		return 0, err
+	}
+	return len(b), nil
 }
 
 func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	if err != nil {
+		return 0, err
+	}
+	return len(s), nil
 }
 
 func (n netConn) close() error {

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3fce00d99e30d66f63f8e3cb85debc137329db0d

元コミット内容

log/syslog: return length of data provided by the user, not length of header

このコミットは、log/syslog パッケージにおいて、ログメッセージを書き込む際に返されるバイト数が、Syslogヘッダーの長さではなく、ユーザーが提供した実際のメッセージデータの長さを返すように修正するものです。

変更の背景

Go言語の log/syslog パッケージは、Syslogプロトコルを使用してログメッセージを送信するための機能を提供します。Syslogメッセージは通常、プライオリティ、タイムスタンプ、ホスト名、アプリケーション名などの情報を含むヘッダーと、実際のメッセージ本文(データ)で構成されます。

fmt.Fprintf 関数は、フォーマットされた文字列を io.Writer に書き込み、書き込まれたバイト数とエラーを返します。以前の実装では、netConn.writeBytes および netConn.writeString メソッドが fmt.Fprintf の戻り値をそのまま返していました。この戻り値には、Syslogヘッダー(プライオリティ、プレフィックス、コロン、スペースなど)とメッセージ本文の合計バイト数が含まれていました。

しかし、ログ書き込み関数(例えば io.Writer インターフェースを実装する関数)の一般的な慣習として、書き込み関数が返すバイト数は、ユーザーが提供した入力データの長さであるべきです。これは、呼び出し元が実際にどれだけのデータが処理されたかを正確に把握し、必要に応じて残りのデータを処理したり、エラーハンドリングを行ったりするために重要です。

この不一致は、log/syslog パッケージを使用するアプリケーションが、書き込み操作の成功を判断したり、送信されたデータの量を正確に追跡したりする際に混乱を招く可能性がありました。例えば、ユーザーが100バイトのメッセージを送信したにもかかわらず、関数が150バイトを返した場合、呼び出し元はSyslogプロトコルの詳細を知らない限り、その差分がどこから来たのか理解できません。

このコミットは、この慣習からの逸脱を修正し、より予測可能で直感的なAPI動作を提供することを目的としています。

前提知識の解説

Syslogプロトコル

Syslogは、システムログメッセージを収集するための標準プロトコルです。RFC 3164(旧)やRFC 5424(新)で定義されています。Syslogメッセージは通常、以下の要素で構成されます。

  • PRI (Priority): メッセージのファシリティ(発生源)と重要度(Severity)を示す数値。<PRI> の形式でメッセージの先頭に付加されます。
  • HEADER: タイムスタンプ、ホスト名、アプリケーション名、プロセスIDなど。
  • MSG (Message): 実際のログメッセージ本文。

Syslogメッセージの一般的なフォーマットは <PRI>HEADER MSG のようになります。このコミットで扱われているのは、特に fmt.Fprintf が書き込む <%d>%s: %s\n の部分で、これは PRIHEADER の一部(プレフィックス)および MSG を含んでいます。

Go言語の io.Writer インターフェース

Go言語では、データを書き込むための汎用的なインターフェースとして io.Writer が定義されています。

type Writer interface {
    Write(p []byte) (n int, err error)
}

Write メソッドは、p からデータを書き込み、書き込まれたバイト数 n とエラー err を返します。この n は、通常、p の長さ(つまり、ユーザーが提供したデータの長さ)と一致することが期待されます。もし n < len(p) であれば、それは部分的な書き込みを示し、呼び出し元は残りのデータを再試行するなどの処理を行う必要があります。

fmt.Fprintf 関数

fmt.Fprintf は、Go言語の fmt パッケージに含まれる関数で、指定された io.Writer にフォーマットされた文字列を書き込みます。

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

この関数は、書き込まれたバイト数 n とエラー err を返します。ここで返される n は、format 文字列と a の値が展開された結果、実際に w に書き込まれた全体のバイト数です。

技術的詳細

このコミットの核心は、fmt.Fprintf の戻り値の解釈と、io.Writer インターフェースの慣習との整合性です。

以前のコードでは、netConn.writeBytesnetConn.writeString は以下のように実装されていました。

func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
}

func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
}

ここで fmt.Fprintf が返す int 値は、Syslogメッセージ全体の長さ(ヘッダー部分 + ユーザーデータ部分)でした。例えば、ユーザーが b または s として "Hello" という文字列(5バイト)を提供し、ヘッダー部分が20バイトだった場合、fmt.Fprintf は25を返していました。

しかし、log/syslog パッケージの Writer インターフェースを実装するこれらのメソッドは、io.WriterWrite メソッドと同様に、ユーザーが提供したデータの長さ(この場合は len(b) または len(s))を返すことが期待されます。

このコミットでは、この期待に応えるために、fmt.Fprintf の戻り値のうち、書き込まれたバイト数 (n) を破棄し、代わりにユーザーが提供したデータの長さ (len(b) または len(s)) を明示的に返すように変更しました。

// 変更前:
// return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)

// 変更後:
_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b) // 書き込まれたバイト数は破棄
if err != nil {
    return 0, err // エラーがあれば0バイトとエラーを返す
}
return len(b), nil // エラーがなければユーザーデータの長さを返す

この変更により、log/syslog パッケージの Writer メソッドは、io.Writer の一般的なセマンティクスに合致し、より予測可能で一貫性のある動作を提供するようになりました。

コアとなるコードの変更箇所

変更は src/pkg/log/syslog/syslog.go ファイル内の netConn 型の以下の2つのメソッドに集中しています。

  1. func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error)
  2. func (n netConn) writeString(p Priority, prefix string, s string) (int, error)

それぞれのメソッドで、fmt.Fprintf の戻り値のうち、書き込まれたバイト数を受け取る変数を _ (ブランク識別子) に変更し、代わりに len(b) または len(s) を返すように修正されています。また、fmt.Fprintf がエラーを返した場合のハンドリングも追加されています。

コアとなるコードの解説

writeBytes メソッドの変更

 func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	if err != nil {
+		return 0, err
+	}
+	return len(b), nil
 }
  • 変更前: fmt.Fprintf の戻り値(書き込まれたバイト数とエラー)をそのまま返していました。このバイト数にはSyslogヘッダーの長さが含まれていました。
  • 変更後:
    • fmt.Fprintf の戻り値のうち、書き込まれたバイト数は _ で破棄されます。
    • err 変数でエラーを受け取ります。
    • if err != nil でエラーチェックを行い、エラーがあれば 0 バイトとエラーを即座に返します。これは、書き込みが失敗した場合は0バイトが処理されたと見なす一般的なパターンです。
    • エラーがなければ、len(b)(ユーザーが提供したバイトスライスの長さ)を返します。これにより、呼び出し元は実際に送信を要求したデータの長さが返されたことを確認できます。

writeString メソッドの変更

 func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	if err != nil {
+		return 0, err
+	}
+	return len(s), nil
 }
  • この変更は writeBytes メソッドと全く同じロジックで、バイトスライス b の代わりに文字列 s を扱っています。
  • len(s)(ユーザーが提供した文字列の長さ)を返すことで、io.Writer のセマンティクスに準拠しています。

これらの変更により、log/syslog パッケージの Writer インターフェースを介してログを書き込むアプリケーションは、より正確で期待通りの戻り値を受け取ることができるようになり、エラーハンドリングやログの追跡が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • RFC 5424 (IETF)
  • Go言語のコミット履歴とコードレビューシステム (golang.org/cl)
  • 一般的なプログラミングにおける write 関数の戻り値に関する慣習
  • fmt.Fprintf の動作に関するGo言語のドキュメント
  • Syslogプロトコルに関する一般的な知識
  • Go言語の io.Writer インターフェースのセマンティクスに関する知識
  • Go言語の _ (ブランク識別子) の使用法に関する知識
  • Go言語のエラーハンドリングの慣習に関する知識
  • GitHubのコミットページ: https://github.com/golang/go/commit/3fce00d99e30d66f63f8e3cb85debc137329db0d
  • Go CL 5644059: https://golang.org/cl/5644059 (これは古いCLシステムへのリンクであり、現在はGerritに移行しているため、直接アクセスできない場合がありますが、コミットメッセージに記載されているため参照しました。)
# [インデックス 11722] ファイルの概要

このコミットは、Go言語の標準ライブラリである `log/syslog` パッケージにおける、`Writer` インターフェースの実装に関する修正です。具体的には、`netConn` 型の `writeBytes` および `writeString` メソッドが返す値が、実際にユーザーが提供したデータの長さを示すように変更されています。

## コミット

commit 3fce00d99e30d66f63f8e3cb85debc137329db0d Author: Rob Pike r@golang.org Date: Thu Feb 9 08:36:13 2012 +1100

log/syslog: return length of data provided by the user, not length of header

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5644059

src/pkg/log/syslog/syslog.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)\n diff --git a/src/pkg/log/syslog/syslog.go b/src/pkg/log/syslog/syslog.go index aef63480f1..700b983c75 100644 --- a/src/pkg/log/syslog/syslog.go +++ b/src/pkg/log/syslog/syslog.go @@ -136,11 +136,19 @@ func (w *Writer) Debug(m string) (err error) { }

func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {

  • return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b)
  • _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, b)
  • if err != nil {
  •   return 0, err
    
  • }
  • return len(b), nil }

func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {

  • return fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s)
  • _, err := fmt.Fprintf(n.conn, "<%d>%s: %s\n", p, prefix, s)
  • if err != nil {
  •   return 0, err
    
  • }
  • return len(s), nil }

func (n netConn) close() error {


## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/3fce00d99e30d66f63f8e3cb85debc137329db0d](https://github.com/golang/go/commit/3fce00d99e30d66f63f8e3cb85debc137329db0d)

## 元コミット内容

`log/syslog: return length of data provided by the user, not length of header`

このコミットは、`log/syslog` パッケージにおいて、ログメッセージを書き込む際に返されるバイト数が、Syslogヘッダーの長さではなく、ユーザーが提供した実際のメッセージデータの長さを返すように修正するものです。

## 変更の背景

Go言語の `log/syslog` パッケージは、Syslogプロトコルを使用してログメッセージを送信するための機能を提供します。Syslogメッセージは通常、プライオリティ、タイムスタンプ、ホスト名、アプリケーション名などの情報を含むヘッダーと、実際のメッセージ本文(データ)で構成されます。

`fmt.Fprintf` 関数は、フォーマットされた文字列を `io.Writer` に書き込み、書き込まれたバイト数とエラーを返します。以前の実装では、`netConn.writeBytes` および `netConn.writeString` メソッドが `fmt.Fprintf` の戻り値をそのまま返していました。この戻り値には、Syslogヘッダー(プライオリティ、プレフィックス、コロン、スペースなど)とメッセージ本文の合計バイト数が含まれていました。

しかし、ログ書き込み関数(例えば `io.Writer` インターフェースを実装する関数)の一般的な慣習として、書き込み関数が返すバイト数は、ユーザーが提供した入力データの長さであるべきです。これは、呼び出し元が実際にどれだけのデータが処理されたかを正確に把握し、必要に応じて残りのデータを処理したり、エラーハンドリングを行ったりするために重要です。

この不一致は、`log/syslog` パッケージを使用するアプリケーションが、書き込み操作の成功を判断したり、送信されたデータの量を正確に追跡したりする際に混乱を招く可能性がありました。例えば、ユーザーが100バイトのメッセージを送信したにもかかわらず、関数が150バイトを返した場合、呼び出し元はSyslogプロトコルの詳細を知らない限り、その差分がどこから来たのか理解できません。

このコミットは、この慣習からの逸脱を修正し、より予測可能で直感的なAPI動作を提供することを目的としています。

## 前提知識の解説

### Syslogプロトコル

Syslogは、システムログメッセージを収集するための標準プロトコルです。RFC 3164(旧)やRFC 5424(新)で定義されています。Syslogメッセージは通常、以下の要素で構成されます。

*   **PRI (Priority)**: メッセージのファシリティ(発生源)と重要度(Severity)を示す数値。`<PRI>` の形式でメッセージの先頭に付加されます。
*   **HEADER**: タイムスタンプ、ホスト名、アプリケーション名、プロセスIDなど。
*   **MSG (Message)**: 実際のログメッセージ本文。

Syslogメッセージの一般的なフォーマットは `<PRI>HEADER MSG` のようになります。このコミットで扱われているのは、特に `fmt.Fprintf` が書き込む `<%d>%s: %s\n` の部分で、これは `PRI` と `HEADER` の一部(プレフィックス)および `MSG` を含んでいます。

### Go言語の `io.Writer` インターフェース

Go言語では、データを書き込むための汎用的なインターフェースとして `io.Writer` が定義されています。

```go
type Writer interface {
    Write(p []byte) (n int, err error)
}

Write メソッドは、p からデータを書き込み、書き込まれたバイト数 n とエラー err を返します。この n は、通常、p の長さ(つまり、ユーザーが提供したデータの長さ)と一致することが期待されます。もし n < len(p) であれば、それは部分的な書き込みを示し、呼び出し元は残りのデータを再試行するなどの処理を行う必要があります。

fmt.Fprintf 関数

fmt.Fprintf は、Go言語の fmt パッケージに含まれる関数で、指定された io.Writer にフォーマットされた文字列を書き込みます。

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

この関数は、書き込まれたバイト数 n とエラー err を返します。ここで返される n は、format 文字列と a の値が展開された結果、実際に w に書き込まれた全体のバイト数です。

技術的詳細

このコミットの核心は、fmt.Fprintf の戻り値の解釈と、io.Writer インターフェースの慣習との整合性です。

以前のコードでは、netConn.writeBytesnetConn.writeString は以下のように実装されていました。

func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
}

func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
}

ここで fmt.Fprintf が返す int 値は、Syslogメッセージ全体の長さ(ヘッダー部分 + ユーザーデータ部分)でした。例えば、ユーザーが b または s として "Hello" という文字列(5バイト)を提供し、ヘッダー部分が20バイトだった場合、fmt.Fprintf は25を返していました。

しかし、log/syslog パッケージの Writer インターフェースを実装するこれらのメソッドは、io.WriterWrite メソッドと同様に、ユーザーが提供したデータの長さ(この場合は len(b) または len(s))を返すことが期待されます。

このコミットでは、この期待に応えるために、fmt.Fprintf の戻り値のうち、書き込まれたバイト数 (n) を破棄し、代わりにユーザーが提供したデータの長さ (len(b) または len(s)) を明示的に返すように変更しました。

// 変更前:
// return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)

// 変更後:
_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b) // 書き込まれたバイト数は破棄
if err != nil {
    return 0, err // エラーがあれば0バイトとエラーを返す
}
return len(b), nil // エラーがなければユーザーデータの長さを返す

この変更により、log/syslog パッケージの Writer メソッドは、io.Writer の一般的なセマンティクスに合致し、より予測可能で一貫性のある動作を提供するようになりました。

コアとなるコードの変更箇所

変更は src/pkg/log/syslog/syslog.go ファイル内の netConn 型の以下の2つのメソッドに集中しています。

  1. func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error)
  2. func (n netConn) writeString(p Priority, prefix string, s string) (int, error)

それぞれのメソッドで、fmt.Fprintf の戻り値のうち、書き込まれたバイト数を受け取る変数を _ (ブランク識別子) に変更し、代わりに len(b) または len(s) を返すように修正されています。また、fmt.Fprintf がエラーを返した場合のハンドリングも追加されています。

コアとなるコードの解説

writeBytes メソッドの変更

 func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, b)
+	if err != nil {
+		return 0, err
+	}
+	return len(b), nil
 }
  • 変更前: fmt.Fprintf の戻り値(書き込まれたバイト数とエラー)をそのまま返していました。このバイト数にはSyslogヘッダーの長さが含まれていました。
  • 変更後:
    • fmt.Fprintf の戻り値のうち、書き込まれたバイト数は _ で破棄されます。
    • err 変数でエラーを受け取ります。
    • if err != nil でエラーチェックを行い、エラーがあれば 0 バイトとエラーを即座に返します。これは、書き込みが失敗した場合は0バイトが処理されたと見なす一般的なパターンです。
    • エラーがなければ、len(b)(ユーザーが提供したバイトスライスの長さ)を返します。これにより、呼び出し元は実際に送信を要求したデータの長さが返されたことを確認できます。

writeString メソッドの変更

 func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
-	return fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s: %s\\n", p, prefix, s)
+	if err != nil {
+		return 0, err
+	}
+	return len(s), nil
 }
  • この変更は writeBytes メソッドと全く同じロジックで、バイトスライス b の代わりに文字列 s を扱っています。
  • len(s)(ユーザーが提供した文字列の長さ)を返すことで、io.Writer のセマンティクスに準拠しています。

これらの変更により、log/syslog パッケージの Writer インターフェースを介してログを書き込むアプリケーションは、より正確で期待通りの戻り値を受け取ることができるようになり、エラーハンドリングやログの追跡が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (pkg.go.dev)
  • RFC 5424 (IETF)
  • Go言語のコミット履歴とコードレビューシステム (golang.org/cl)
  • 一般的なプログラミングにおける write 関数の戻り値に関する慣習
  • fmt.Fprintf の動作に関するGo言語のドキュメント
  • Syslogプロトコルに関する一般的な知識
  • Go言語の io.Writer インターフェースのセマンティクスに関する知識
  • Go言語の _ (ブランク識別子) の使用法に関する知識
  • Go言語のエラーハンドリングの慣習に関する知識
  • GitHubのコミットページ: https://github.com/golang/go/commit/3fce00d99e30d66f63f8e3cb85debc137329db0d
  • Go CL 5644059: https://golang.org/cl/5644059 (これは古いCLシステムへのリンクであり、現在はGerritに移行しているため、直接アクセスできない場合がありますが、コミットメッセージに記載されているため参照しました。)