[インデックス 16865] ファイルの概要
このコミットは、Go言語の標準ライブラリ log/syslog
パッケージにおける serverConn
インターフェースの利用を復元するものです。以前のコミットで削除されたこのインターフェースは、特にSolaris上のgccgo環境において、syslogデーモンへの接続方法の特殊性に対応するために必要とされていました。
コミット
commit 0738c7e977b3ac190d6176555b69c8e9d2fb4de6
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jul 24 10:28:57 2013 -0700
log/syslog: restore use of serverConn interface
Revision 15629 (8d71734a0cb0) removed the serverConn interface
that was introduce in revision 7718 (ee5e80c62862). The
serverConn interface was there for use by gccgo on Solaris,
and it is still needed there. Solaris does not support
connecting to the syslog daemon over TCP, and gccgo simply
calls the C library function. This CL restores the
interface.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/11737043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0738c7e977b3ac190d6176555b69c8e9d2fb4de6
元コミット内容
log/syslog: restore use of serverConn interface
このコミットは、log/syslog
パッケージにおいて serverConn
インターフェースの使用を復元することを目的としています。以前のコミット (リビジョン 15629, ハッシュ 8d71734a0cb0) で削除されたこのインターフェースは、元々リビジョン 7718 (ハッシュ ee5e80c62862) で導入されたものでした。このインターフェースは、Solaris上でgccgoを使用する際に必要とされており、SolarisがTCP経由でのsyslogデーモンへの接続をサポートしていないため、gccgoがCライブラリ関数を直接呼び出す必要があるという特殊な状況に対応するためのものでした。この変更は、そのインターフェースを再度導入するものです。
変更の背景
Go言語の log/syslog
パッケージは、システムログサービスへのメッセージ送信を抽象化します。しかし、異なるオペレーティングシステムやコンパイラ環境では、syslogデーモンとの通信方法に差異が生じることがあります。
このコミットの背景には、以下の経緯があります。
-
serverConn
インターフェースの導入 (リビジョン 7718, ee5e80c62862): 初期の段階で、特定の環境(特にSolaris上のgccgo)でのsyslog通信の特殊性を吸収するためにserverConn
インターフェースが導入されました。これは、Goの標準的なnet.Conn
インターフェースでは対応できない、Cライブラリの直接呼び出しが必要なケースを扱うためと考えられます。 -
serverConn
インターフェースの削除 (リビジョン 15629, 8d71734a0cb0): 何らかの理由(おそらく、コードの簡素化や、特定の環境への依存を減らす目的)で、このserverConn
インターフェースが削除されました。しかし、この削除はSolaris上のgccgo環境におけるsyslog機能に影響を与えました。 -
問題の再発と
serverConn
の復元:serverConn
インターフェースの削除後、Solaris上のgccgo環境でsyslogが正しく機能しないという問題が再発しました。SolarisはTCP経由でのsyslogデーモンへの接続をサポートしておらず、gccgoはCライブラリのsyslog関数を直接呼び出す必要があったため、このインターフェースが不可欠であることが判明しました。このコミットは、この問題を解決するためにserverConn
インターフェースを復元し、Solaris環境での互換性を確保することを目的としています。
前提知識の解説
syslog
syslogは、UNIX系オペレーティングシステムでシステムログメッセージを生成、保存、管理するための標準的なプロトコルです。アプリケーションやシステムプロセスは、syslogプロトコルを使用してイベントメッセージをsyslogデーモンに送信し、デーモンはこれらのメッセージをファイル、コンソール、またはリモートのsyslogサーバーに書き込みます。
Go言語の log/syslog
パッケージ
Go言語の標準ライブラリには、syslogプロトコルを介してシステムログにメッセージを書き込むための log/syslog
パッケージが含まれています。このパッケージは、Dial
関数を通じてsyslogデーモンへの接続を確立し、Writer
型を通じてメッセージを送信する機能を提供します。
net.Conn
インターフェース
Go言語の net
パッケージには、ネットワーク接続を表す net.Conn
インターフェースが定義されています。これは、Read
、Write
、Close
などの基本的なネットワーク操作を提供し、TCPやUDPなどの様々なネットワークプロトコルに共通のインターフェースを提供します。
gccgo
gccgoは、GCC (GNU Compiler Collection) のフロントエンドとして実装されたGo言語のコンパイラです。Go言語の公式コンパイラであるgcとは異なり、gccgoはGCCのバックエンドを利用するため、様々なアーキテクチャやオペレーティングシステムへの移植が容易です。しかし、その実装の違いから、特定のシステムコールやライブラリの扱いに差異が生じることがあります。
Solarisにおけるsyslog接続の特殊性
一般的なLinuxシステムなどでは、syslogデーモンへの接続はUnixドメインソケット(/dev/log
や /var/run/syslog
など)やUDP/TCPポート(通常は514番)を介して行われます。しかし、Solarisでは、TCP経由でのsyslogデーモンへの直接接続がサポートされていない場合があります。代わりに、C標準ライブラリの syslog(3)
関数を呼び出すことが推奨されるか、あるいはそれが唯一の手段となることがあります。gccgoのようなコンパイラは、このようなプラットフォーム固有の制約に対応するために、Cライブラリの関数を直接呼び出すメカニズムを必要とします。
技術的詳細
このコミットの核心は、Goのインターフェースの柔軟性を利用して、プラットフォーム固有のsyslog通信メカニズムを抽象化することにあります。
serverConn
インターフェースの定義
type serverConn interface {
writeString(p Priority, hostname, tag, s, nl string) error
close() error
}
このインターフェースは、syslogメッセージを書き込むための writeString
メソッドと、接続を閉じるための close
メソッドを定義しています。これにより、具体的な接続の実装(例: net.Conn
を使用したネットワーク接続、またはCライブラリ呼び出し)を抽象化できます。
netConn
構造体の導入と serverConn
インターフェースの実装
type netConn struct {
conn net.Conn
}
func (n netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
timestamp := time.Now().Format(time.RFC3339)
_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, hostname,
tag, os.Getpid(), msg, nl)
return err
}
func (n netConn) close() error {
return n.conn.Close()
}
netConn
構造体は、内部に標準の net.Conn
を保持し、この netConn
が serverConn
インターフェースを実装します。これにより、通常のネットワーク接続を介したsyslog通信が serverConn
インターフェースを通じて行えるようになります。writeString
メソッドでは、syslogメッセージのフォーマット(RFC3339タイムスタンプ、ホスト名、タグ、PIDなどを含む)を行い、内部の net.Conn
に書き込んでいます。
Writer
構造体の変更
type Writer struct {
// ...
mu sync.Mutex // guards conn
conn serverConn // Changed from net.Conn to serverConn
}
Writer
構造体の conn
フィールドの型が net.Conn
から serverConn
インターフェースに変更されました。これにより、Writer
は serverConn
インターフェースを満たす任意の型(netConn
や、Solaris上のgccgoが提供するカスタム実装など)を扱うことができるようになります。
connect()
メソッドの変更
func (w *Writer) connect() (err error) {
// ...
if w.conn != nil {
w.conn.close() // Call close() method of serverConn interface
w.conn = nil
}
// ...
if err == nil {
w.conn = netConn{c} // Wrap net.Conn with netConn to satisfy serverConn
// ...
}
// ...
}
connect()
メソッド内で、既存の接続を閉じる際に w.conn.Close()
ではなく w.conn.close()
が呼び出されるようになりました。これは、conn
が serverConn
インターフェース型になったためです。
また、net.Dial
で取得した net.Conn
オブジェクトを w.conn
に代入する際に、netConn{c}
のように netConn
構造体でラップするようになりました。これにより、net.Conn
が serverConn
インターフェースを満たすようになります。
Close()
メソッドの変更
func (w *Writer) Close() error {
// ...
if w.conn != nil {
err := w.conn.close() // Call close() method of serverConn interface
w.conn = nil
return err
}
// ...
}
Close()
メソッドでも同様に、w.conn.Close()
ではなく w.conn.close()
が呼び出されるようになりました。
write()
メソッドの変更
func (w *Writer) write(p Priority, msg string) (int, error) {
// ...
err := w.conn.writeString(p, w.hostname, w.tag, msg, nl) // Call writeString() method
if err != nil {
return 0, err
}
// ...
}
write()
メソッドでは、直接 fmt.Fprintf
を使用して w.conn
に書き込む代わりに、w.conn.writeString()
メソッドを呼び出すようになりました。これにより、実際の書き込みロジックが serverConn
インターフェースの実装に委譲されます。
syslog_unix.go
の変更
func unixSyslog() (conn serverConn, err error) { // Return type changed to serverConn
// ...
if err == nil {
return netConn{conn}, nil // Wrap net.Conn with netConn
}
// ...
}
syslog_unix.go
ファイルの unixSyslog
関数の戻り値の型が net.Conn
から serverConn
に変更されました。これにより、Unixドメインソケット経由で接続が確立された場合も、その接続が netConn
でラップされて serverConn
インターフェースとして返されるようになります。
Solaris固有の実装(想定)
このコミットメッセージによると、Solaris上のgccgoは syslog_solaris.go
というファイルで unixSyslog
を実装し、serverConn
インターフェースを満たす型を返すことで、Cライブラリのsyslog関数を呼び出すと説明されています。このコミットは、そのSolaris固有の実装が機能するために必要な serverConn
インターフェースを復元するものです。
コアとなるコードの変更箇所
src/pkg/log/syslog/syslog.go
Writer
構造体のconn
フィールドの型がnet.Conn
からserverConn
インターフェースに変更。serverConn
インターフェースが定義された。netConn
構造体が定義され、serverConn
インターフェースを実装。Writer.connect()
メソッド内で、w.conn.Close()
がw.conn.close()
に、w.conn = c
がw.conn = netConn{c}
に変更。Writer.Close()
メソッド内で、w.conn.Close()
がw.conn.close()
に変更。Writer.write()
メソッド内で、fmt.Fprintf
による直接書き込みがw.conn.writeString()
の呼び出しに変更。
src/pkg/log/syslog/syslog_unix.go
unixSyslog()
関数の戻り値の型がnet.Conn
からserverConn
に変更。net.Dial
で取得したnet.Conn
をnetConn{conn}
でラップして返すように変更。
コアとなるコードの解説
このコミットの主要な目的は、Goの log/syslog
パッケージが、標準的なネットワーク接続だけでなく、Solaris上のgccgoのようにCライブラリを直接呼び出す必要がある特殊な環境でも動作するようにすることです。
-
インターフェースによる抽象化:
serverConn
インターフェースを導入することで、syslogメッセージの書き込みと接続のクローズという共通の操作を抽象化しています。これにより、Writer
型は具体的な接続の実装(TCP/UDPソケット、Unixドメインソケット、またはCライブラリ呼び出し)に依存せず、serverConn
インターフェースを満たす任意のオブジェクトを扱うことができます。 -
netConn
による標準実装のラップ:netConn
構造体は、Goの標準的なnet.Conn
をラップし、serverConn
インターフェースを実装します。これにより、既存のネットワークベースのsyslog通信は、この新しいインターフェースを通じて透過的に機能し続けます。 -
Writer
の汎用化:Writer
構造体のconn
フィールドがserverConn
インターフェース型になったことで、Writer
はより汎用的なsyslogクライアントとして機能します。connect()
メソッドで接続を確立する際、通常のネットワーク接続であればnetConn
でラップされたオブジェクトがconn
に設定され、Solarisのような特殊な環境では、その環境固有のserverConn
実装が設定されることになります。 -
書き込みロジックの委譲:
write()
メソッドがw.conn.writeString()
を呼び出すようになったことで、実際のメッセージフォーマットと書き込み処理はserverConn
インターフェースの実装に委譲されます。これにより、各実装は自身の最適な方法でsyslogメッセージを送信できます。例えば、netConn
はfmt.Fprintf
を使ってソケットに書き込み、Solarisのカスタム実装はCライブラリのsyslog(3)
関数を呼び出す、といった具合です。
この変更により、log/syslog
パッケージは、異なるプラットフォームやコンパイラ環境におけるsyslog通信の差異を吸収し、より堅牢で移植性の高いコードベースとなりました。
関連リンク
- Go言語の
log/syslog
パッケージのドキュメント: https://pkg.go.dev/log/syslog - Go言語の
net
パッケージのドキュメント: https://pkg.go.dev/net - GCCGoプロジェクト: https://gcc.gnu.org/go/
- Solarisオペレーティングシステムに関する情報 (一般的な情報源)
参考にした情報源リンク
- Go言語の公式リポジトリ (GitHub): https://github.com/golang/go
- Go言語のコードレビューシステム (Gerrit): https://go.dev/cl/ (コミットメッセージに記載されている
https://golang.org/cl/11737043
はGerritの変更リストへのリンクです) - syslogプロトコルに関するRFC (例: RFC 5424)
- Go言語のインターフェースに関する公式ドキュメントやチュートリアル
- Solarisのシステムプログラミングに関するドキュメント (一般的な情報源)
- GCCGoの内部実装に関する情報 (一般的な情報源)
- Go言語のコミット履歴 (GitHubまたはGerrit)
- Go言語のIssueトラッカー (問題報告や議論の履歴)
- Go言語のメーリングリスト (golang-devなど)