TWSNMPのログを圧縮して保存する
復刻版のTWSNMPをしばらく使っているとデータベースのサイズが大きくなっていくのが気になってきました。大部分は、syslogとNetflowのデータを保存していることです。保存期間を短くすればよいのですが、ログは1年ぐらいさかのぼって調べることもあるので何とかしようと思いました。
一番、先に思いつく方法は、圧縮して保存することです。使っているデータベースの機能で圧縮できれば簡単なのですが、そう都合のよい機能はありません。そこで自分で作ることにしました。
最初の試作で途方にくれる
GO言語には、私を助けてくれる魔法のパッケージが沢山あるのでデータの圧縮ぐらい簡単にできるだろうと思っていました。
ありました
です。サンプルみて簡単そうなので喜んでテストプログラムを作ってみました。
「あれ、サイズ大きくなっている」
そうです、ログを圧縮したデータが元のサイズより大きくなっていたのです。これじゃ圧縮機能つけないほうがよいのかもとがっかりした気持ちで昨日は寝ました。夜中にトイレに起きた時、かみさんは、まだリビングにいましたが、猫だけ何か言いながら自分の部屋へ行くところでした。
再チャレンジ
今朝、5時半に猫に起こされて、安易にライブラリ使えば簡単にできるほど甘くないと思い、もう一度考えることにしました。データを少しずづ変えながら圧縮されたデータを眺めていると圧縮したデータにはヘッダがついていて、その分サイズが大きくなっていました。そこで、ヘッダを削るとか自分で加工する方法も考えましたが、いまいちスッキリしません。悩むこと一時間、その間、猫は大騒ぎしてました。「この前の雪は、今日はないのか?」ベランダや庭を見せてやっと納得したようで寝ました。
希望の光
いろいろ、調べていると
を見つけました。ありがたいことに、同じようなことで先に悩んでくれて、メモまで残してくれました。
RFC 1950, RFC 1951, RFC 1952 はデータのフォーマットが違うだけで圧縮アルゴリズムは同じ。 なので,単純にデータを圧縮するだけなら RFC 1951 でいいという話になる。
ということを知りました。再び ありがとう。
開発の方向性
というわけでパッケージは、
を使うことにしました。
処理の速度や圧縮率を考えて
・サイズの小さいログは圧縮しない。
・圧縮ログと非圧縮ログをデータベースで混在できるようにする
ということで作ってみようと思います。
使う人からみるとログの圧縮に関して何も気にする必要がないよう見えるけどソフトウェアの中では、いい感じに処理しているということです。
混在する方法
データベースには、圧縮したログとしていないログが混在できるようにするためには、識別する方法が必要です。最初に考えたgzip形式を使えば、データの先頭にマジックナンバーがついているので、簡単に識別できます。flateにするとこれがなくなるので、ちょっと困ったのですが、flateパッケージのソースコードを読むと最後に0バイトのブロックを書いていので、これを利用することにしました。詳しく知りたい人は、テストプログラムを見てください。
作ったテストプログラム
プログラムに興味のある人向けに、作ったテストプログラムを掲載しておきます。
package main
import (
"bytes"
"compress/flate"
"compress/gzip"
"fmt"
"io/ioutil"
)
func testGZIP(s string) {
var b bytes.Buffer
gz := gzip.NewWriter(&b)
if _, err := gz.Write([]byte(s)); err != nil {
panic(err)
}
if err := gz.Flush(); err != nil {
panic(err)
}
if err := gz.Close(); err != nil {
panic(err)
}
glen := b.Len()
fmt.Println(b.Bytes())
r, _ := gzip.NewReader(&b)
d, _ := ioutil.ReadAll(r)
fmt.Printf("slen=%d glen=%d\n", len(s), glen)
}
func testFlate(s string) {
var b bytes.Buffer
f, _ := flate.NewWriter(&b, flate.DefaultCompression)
if _, err := f.Write([]byte(s)); err != nil {
panic(err)
}
if err := f.Flush(); err != nil {
panic(err)
}
if err := f.Close(); err != nil {
panic(err)
}
flen := b.Len()
fmt.Println(b.Bytes())
r := flate.NewReader(&b)
d, _ := ioutil.ReadAll(r)
fmt.Printf("slen=%d flen=%d\n", len(s), flen)
}
func main() {
// サイズが小さくなる例
s := "2020-04-04T05:55:04.952410+09:00 test hwmon: CORE0_TEMP=35.000 CORE1_TEMP=35.000 SYS_TEMP=38.000 CPU_TEMP=46.000 FAN1=3708 Vcore=0.784 AVCC=3.264 3VCC=3.248 1.8V=1.536 1.5V=1.016 VSB=3.264 VBAT=3.232"
testGZIP(s)
testFlate(s)
// サイズが大きくなる例
s = "Event: write, Path: /BACKUP/YMIMacBook.backupbundle/bands/29, File/Folder: File, Size: 32.35 MB, User: ymi, IP: 192.168.1.16"
testGZIP(s)
testFlate(s)
}
実行すると
% go run main.go
[31 139 8 0 0 0 0 0 0 255 84 138 63 11 194 48 20 7 191 202 219 165 225 151 228 189 52 13 100 104 75 221 212 98 107 192 201 65 10 46 90 208 130 95 95 250 103 113 187 59 206 192 32 3 103 224 30 18 68 2 88 21 98 88 99 135 34 0 52 13 159 137 30 223 231 248 10 68 245 233 220 224 214 55 135 54 90 81 0 150 162 255 74 119 237 54 247 235 209 94 86 103 183 248 190 60 234 104 115 120 74 247 241 61 68 168 220 51 149 169 174 163 85 198 49 217 13 217 147 86 62 69 173 196 58 210 74 102 132 118 148 186 106 91 83 85 246 51 90 243 3 0 0 255 255 1 0 0 255 255 170 219 219 204 200 0 0 0]
slen=200 glen=168
[84 138 63 11 194 48 20 7 191 202 219 165 225 151 228 189 52 13 100 104 75 221 212 98 107 192 201 65 10 46 90 208 130 95 95 250 103 113 187 59 206 192 32 3 103 224 30 18 68 2 88 21 98 88 99 135 34 0 52 13 159 137 30 223 231 248 10 68 245 233 220 224 214 55 135 54 90 81 0 150 162 255 74 119 237 54 247 235 209 94 86 103 183 248 190 60 234 104 115 120 74 247 241 61 68 168 220 51 149 169 174 163 85 198 49 217 13 217 147 86 62 69 173 196 58 210 74 102 132 118 148 186 106 91 83 85 246 51 90 243 3 0 0 255 255 1 0 0 255 255]
slen=200 flen=150
[31 139 8 0 0 0 0 0 0 255 28 201 209 10 194 32 20 0 208 95 185 31 32 138 142 70 187 111 25 13 70 8 66 236 161 71 157 23 146 153 198 230 138 250 250 168 199 195 57 61 41 87 132 215 18 43 49 176 174 222 16 132 62 28 207 163 21 87 51 24 55 233 82 102 238 221 52 111 15 191 229 144 72 120 151 195 42 84 199 160 143 137 68 95 82 160 5 255 96 112 137 31 66 104 20 111 118 96 52 131 113 253 221 251 30 25 12 22 65 118 138 203 118 207 37 151 237 23 0 0 255 255 1 0 0 255 255 9 55 120 41 124 0 0 0]
slen=124 glen=145
[28 201 209 10 194 32 20 0 208 95 185 31 32 138 142 70 187 111 25 13 70 8 66 236 161 71 157 23 146 153 198 230 138 250 250 168 199 195 57 61 41 87 132 215 18 43 49 176 174 222 16 132 62 28 207 163 21 87 51 24 55 233 82 102 238 221 52 111 15 191 229 144 72 120 151 195 42 84 199 160 143 137 68 95 82 160 5 255 96 112 137 31 66 104 20 111 118 96 52 131 113 253 221 251 30 25 12 22 65 118 138 203 118 207 37 151 237 23 0 0 255 255 1 0 0 255 255]
slen=124 flen=127
つづく
開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。