В моем текущем проекте Golang я использую буферизацию журналов перед отправкой их в Elasticsearch в моей библиотеке журналов. Я хочу представить что-то вроде функции C atexit()
для очистки всех ожидающих журналов в случае неожиданного выхода.
Я нашел библиотеку atexit , но в моем случае ее было недостаточно, поскольку она не позволяет передавать аргументы в функции-обработчики. Я решил написать свою версию и в итоге получил примерно аналогичную структуру. У меня есть модуль atexit:
package atexit
import (
"fmt"
"os"
"reflect"
)
var handlers []interface{}
var params []interface{}
func runHandler(handler interface{}, p interface{}) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintln(os.Stderr, "error: atexit handler error:", err)
}
}()
f := reflect.ValueOf(handler)
s := reflectSlice(p)
fmt.Printf("%#v", s)
f.Call(s)
}
func reflectSlice(slice interface{}) []reflect.Value {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("InterfaceSlice() given a non-slice type")
}
ret := make([]reflect.Value, s.Len())
for i:=0; i<s.Len(); i++ {
ret[i] = reflect.ValueOf(s.Index(i))
}
return ret
}
func runHandlers() {
for i, handler := range handlers {
runHandler(handler, params[i])
}
}
func Exit(code int) {
runHandlers()
os.Exit(code)
}
func Register(handler interface{}, p interface{}) {
f := reflect.TypeOf(handler)
if f.Kind() != reflect.Func {
panic("Register() given a non-function type")
}
handlers = append(handlers, handler)
params = append(params, p)
}
и я звоню из основной программы:
package main
import (
"fmt"
"./atexit"
"encoding/json"
"reflect"
)
type Batch struct {
Index string `json:"index"`
Type string `json:"_Type"`
Content interface{} `json:"Content"`
}
func flush(b ...interface{}) {
for _, entry := range(b) {
fmt.Printf("%#v\n", entry)
fmt.Println(reflect.TypeOf(reflect.ValueOf(entry)))
fmt.Println(reflect.TypeOf(entry))
a, err := json.Marshal(entry)
if err != nil {
fmt.Println("error!")
}
fmt.Println(string(a))
}
}
func handler(v ...interface{}) {
fmt.Println("Exiting")
for _, batch := range(v) {
fmt.Printf("%#v\n", batch)
}
}
func main() {
type ColorGroup struct {
ID int
Name string
Colors []string
}
group := ColorGroup{
ID: 1,
Name: "Reds",
Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
fmt.Printf("%#v\n", group)
r, err := json.Marshal(group)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(r))
b := []Batch{Batch{"index", "type", "content1"},Batch{"index", "type", "content2"}}
atexit.Register(handler, b)
atexit.Register(flush, []ColorGroup{group})
atexit.Exit(0)
}
Как видите, вызывая reflect.ValueOf()
, я получаю структуру reflect.Value
, которая затем передается функции обратного вызова. Кажется, проблема в том, что эта структура не содержит метаданных об экспорте json или неправильно обрабатывается с помощью json.Marshal()
, который затем выводит пустой json. Есть ли способ, которым я могу передать правильную структуру []interface{}
в функцию обратного вызова или какой-то подобный механизм, который будет делать примерно то, что я пытаюсь достичь? Обратите внимание, что я хочу общий механизм обратного вызова, который не должен зависеть от типа, передаваемого ему. Пока что это кажется невозможным или, по крайней мере, очень ограниченным f.Call(s)
, который вызывается в runHandler()
и в качестве параметра принимается отражение. Значение.