govipsのlog levelを設定する

バックエンドにlibvipsを使っているGoの画像処理パッケージ
今回はこのライブラリのログレベル設定についてメモを残す

特に何もせずに実行すると以下のように大量のログがstderrに放出される。
開発環境やデバッグ中は助かるが、production環境でこれらが放出されるのは困りそう。なので、LogLevelを設定する方法を調べていく。
container1-1 | 2025/03/16 22:58:31 [VIPS.info] g_getenv( "PATH" ) == "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/usr/local/sbin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/usr/local/bin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/usr/sbin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/usr/bin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/sbin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] looking in "/bin" for "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] trying for dir = "//govips", name = "govips"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] canonicalised path = ""
container1-1 | 2025/03/16 22:58:31 [VIPS.info] VIPS_PREFIX = /usr
container1-1 | 2025/03/16 22:58:31 [VIPS.info] VIPS_LIBDIR = /usr/lib/aarch64-linux-gnu
container1-1 | 2025/03/16 22:58:31 [VIPS.info] prefix = /usr
container1-1 | 2025/03/16 22:58:31 [VIPS.info] libdir = /usr/lib/aarch64-linux-gnu
container1-1 | 2025/03/16 22:58:31 [VIPS.info] searching "/usr/lib/aarch64-linux-gnu/vips-modules-8.14"
container1-1 | 2025/03/16 22:58:31 [VIPS.info] searching "/usr/lib/aarch64-linux-gnu/vips-plugins-8.14"
container1-1 | 2025/03/16 22:58:31 [govips.info] vips 8.14.1 started with concurrency=1 cache_max_files=0 cache_max_mem=52428800 cache_max=100
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=jp2k
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=png
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=tiff
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=gif
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=jpeg
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=svg
container1-1 | 2025/03/16 22:58:31 [govips.info] registered image type loader type=webp
container1-1 | 2025/03/16 22:58:31 [VIPS.info] selected loader is image source
container1-1 | 2025/03/16 22:58:31 [VIPS.info] input size is 960 x 540
container1-1 | 2025/03/16 22:58:31 [VIPS.info] loading with factor 1 pre-shrink
container1-1 | 2025/03/16 22:58:31 [VIPS.info] pre-shrunk size is 960 x 540
container1-1 | 2025/03/16 22:58:31 [VIPS.info] converting to processing space srgb
container1-1 | 2025/03/16 22:58:31 [VIPS.info] residual reducev by 0.37037
container1-1 | 2025/03/16 22:58:31 [VIPS.info] reducev: 17 point mask
container1-1 | 2025/03/16 22:58:31 [VIPS.info] reducev: using vector path
container1-1 | 2025/03/16 22:58:31 [VIPS.info] residual reduceh by 0.37037
container1-1 | 2025/03/16 22:58:31 [VIPS.info] reduceh: 17 point mask
container1-1 | 2025/03/16 22:58:31 [VIPS.info] converting to output space srgb
container1-1 | 2025/03/16 22:58:31 [VIPS.info] cropping to 200x200

Logハンドラーのセッターはあるっぽいのでこれを深堀りする。
LoggingHandlerFunction is a function which will be called for each log message. By default, govips sends logging messages to os.Stderr. If you want to log elsewhere such as to a file or to a state variable which you inspect yourself, define a new logging handler function and set it via LoggingSettings().
- デフォルトではstderrにログを出力する
- ファイルなど他のところに出力してほしいときは、LoggingHandlerFuntion型の関数を定義して、LoggingSetting関数から設定してね
とのこと

const (
LogLevelError LogLevel = C.G_LOG_LEVEL_ERROR
LogLevelCritical LogLevel = C.G_LOG_LEVEL_CRITICAL
LogLevelWarning LogLevel = C.G_LOG_LEVEL_WARNING
LogLevelMessage LogLevel = C.G_LOG_LEVEL_MESSAGE
LogLevelInfo LogLevel = C.G_LOG_LEVEL_INFO
LogLevelDebug LogLevel = C.G_LOG_LEVEL_DEBUG
)
- LogLevelは5種類ある模様。
- production環境ではError, Critical, Warningあたりを出力して、あとのログは捨てても良いかもしれない(ご自分の要件に応じて適切に設定してくださいね)

govipsがデフォルトで設定してくれるロギングハンドラの実装。stdlibのlogパッケージを使ってシンプルに実装している。(logパッケージを使っているからstderrに出力するのね。納得)
このデフォルトのロギングハンドラはprivate関数になっているので、直接プロジェクトファイルで使うことはできない。というわけで、独自のロギングハンドラを実装していく

最終的にはこのような形でサンプルを作ってみた
すべてのログレベルに対して、定義したハンドラをセットするイメージ。loglevel別にハンドラを作りたいなら、それぞれ作ればカスタマイズできそう
APP_ENVがproduction以外のときには、デフォルトのハンドラをそのまま使う構成にしている
package main
import (
"fmt"
"log"
"os"
"github.com/caarlos0/env/v11"
"github.com/davidbyttow/govips/v2/vips"
)
type Config struct {
AppEnv string `env:"APP_ENV"`
}
var cfg Config
func init() {
err := env.Parse(&cfg)
if err != nil {
log.Fatalf("%+v\n", err)
}
}
func loggingHandler(messageDomain string, messageLevel vips.LogLevel, message string) {
var messageLevelDescription string
switch messageLevel {
case vips.LogLevelError:
messageLevelDescription = "error"
case vips.LogLevelCritical:
messageLevelDescription = "critical"
case vips.LogLevelWarning:
messageLevelDescription = "warning"
default:
return
}
log.Printf("[%v.%v] %v", messageDomain, messageLevelDescription, message)
}
func main() {
vips.Startup(nil)
defer vips.Shutdown()
// NOTE: In production, only log errors and critical, warning messages
if cfg.AppEnv == "production" {
vips.LoggingSettings(loggingHandler, vips.LogLevelError)
vips.LoggingSettings(loggingHandler, vips.LogLevelCritical)
vips.LoggingSettings(loggingHandler, vips.LogLevelWarning)
vips.LoggingSettings(loggingHandler, vips.LogLevelMessage)
vips.LoggingSettings(loggingHandler, vips.LogLevelInfo)
vips.LoggingSettings(loggingHandler, vips.LogLevelDebug)
}
image1, err := vips.NewImageFromFile("./input.png")
if err != nil {
log.Fatalf("error loading image: %v", err)
}
err = image1.Thumbnail(200, 200, vips.InterestingCentre)
if err != nil {
log.Fatalf("error resizing image: %v", err)
}
ep := vips.NewPngExportParams()
b, _, err := image1.ExportPng(ep)
if err != nil {
log.Fatalf("error exporting image: %v", err)
}
err = os.WriteFile("./output.png", b, 0644)
if err != nil {
log.Fatalf("error writing image: %v", err)
}
}

サンプルの実行結果はこのような感じ.
registered image type loader ...以下のログがAPP_ENV="production"のときは出力されないようになっている
container1-1 | 2025/03/16 23:15:29 [VIPS.info] g_getenv( "PATH" ) == "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/usr/local/sbin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/usr/local/bin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/usr/sbin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/usr/bin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/sbin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] looking in "/bin" for "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] trying for dir = "//govips", name = "govips"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] canonicalised path = ""
container1-1 | 2025/03/16 23:15:29 [VIPS.info] VIPS_PREFIX = /usr
container1-1 | 2025/03/16 23:15:29 [VIPS.info] VIPS_LIBDIR = /usr/lib/aarch64-linux-gnu
container1-1 | 2025/03/16 23:15:29 [VIPS.info] prefix = /usr
container1-1 | 2025/03/16 23:15:29 [VIPS.info] libdir = /usr/lib/aarch64-linux-gnu
container1-1 | 2025/03/16 23:15:29 [VIPS.info] searching "/usr/lib/aarch64-linux-gnu/vips-modules-8.14"
container1-1 | 2025/03/16 23:15:29 [VIPS.info] searching "/usr/lib/aarch64-linux-gnu/vips-plugins-8.14"
container1-1 | 2025/03/16 23:15:29 [govips.info] vips 8.14.1 started with concurrency=1 cache_max_files=0 cache_max_mem=52428800 cache_max=100
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=svg
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=jp2k
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=png
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=tiff
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=webp
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=gif
container1-1 | 2025/03/16 23:15:29 [govips.info] registered image type loader type=jpeg
ちなみに、registered image typeまでのログはvips.Startupで起動する際に設定されているので、ユーザー側では抑制できないかもしれない。あっても困らないし、出力してくれていいと思うのでこれにて終了

govipsはCGoを使って作られているパッケージなので、マルチステージビルドでproduction用のイメージを作っているときは少しだけ注意が必要だった
これについては近いうちに記事にしようと思う。