/
mongolog.go
141 lines (123 loc) · 3.61 KB
/
mongolog.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package mongolog
import (
"context"
"fmt"
"os"
"time"
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func NewHook(h string, p string, u string, pass string, db string, coll string) (*hook, error) {
cs := "mongodb://" + u + ":" + pass + "@" + h + ":" + p + "/?retryWrites=true&w=majority"
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(cs))
if err != nil {
return nil, err
}
return newHookStruct(client.Database(db).Collection(coll)), nil
}
func NewHookConnectionString(cs string, db string, coll string) (*hook, error) {
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(cs))
if err != nil {
return nil, err
}
return newHookStruct(client.Database(db).Collection(coll)), nil
}
func NewHookClient(client *mongo.Client, db string, coll string) (*hook, error) {
return newHookStruct(client.Database(db).Collection(coll)), nil
}
func NewHookDatabase(database *mongo.Database, coll string) (*hook, error) {
return newHookStruct(database.Collection(coll)), nil
}
func NewHookCollection(collection *mongo.Collection) (*hook, error) {
return newHookStruct(collection), nil
}
/**
* Hook struct for Logrus hook interface
**/
type hook struct {
c *mongo.Collection
isAsync bool
writeTimeout time.Duration
ctx context.Context
failoverFile *os.File
}
// Function to create struct with default value
func newHookStruct(C *mongo.Collection) *hook {
return &hook{
c: C,
isAsync: false,
writeTimeout: 0,
ctx: context.Background(),
failoverFile: nil,
}
}
func (h *hook) Fire(entry *logrus.Entry) error {
if h.isAsync {
go h.fireProcess(entry)
return nil
} else {
return h.fireProcess(entry)
}
}
func (h *hook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *hook) SetIsAsync(IsAsync bool) {
h.isAsync = IsAsync
}
func (h *hook) SetWriteTimeout(Dur time.Duration) {
h.writeTimeout = Dur
}
func (h *hook) SetContext(Ctx context.Context) {
h.ctx = Ctx
}
func (h *hook) SetFailoverFilePath(F string) error {
file, err := os.OpenFile(F, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return fmt.Errorf("failed to set failover file: %v", err)
}
h.failoverFile = file
return nil
}
// Private function for internal process
func (h *hook) fireProcess(entry *logrus.Entry) error {
ctx := h.ctx
// If write timeout greater than 0
if h.writeTimeout > 0 {
var ctxCancelFunc context.CancelFunc
ctx, ctxCancelFunc = context.WithTimeout(ctx, h.writeTimeout)
defer ctxCancelFunc()
}
data := make(logrus.Fields)
data["level"] = entry.Level.String()
data["time"] = entry.Time
data["message"] = entry.Message
failoverText := entry.Time.String() + "\t" + entry.Level.String() + "\t" + entry.Message
for k, v := range entry.Data {
if errData, isError := v.(error); logrus.ErrorKey == k && v != nil && isError {
data[k] = errData.Error()
failoverText += "\t" + errData.Error()
} else {
data[k] = v
valStr := fmt.Sprint(v)
failoverText += "\t" + valStr
}
}
_, err := h.c.InsertOne(ctx, data)
if err != nil {
// If failoverFile is setted then append to text
if h.failoverFile != nil {
// Write log to file
if _, err := h.failoverFile.WriteString(failoverText + "\n"); err != nil {
return fmt.Errorf("failed to save log to failoverfile: %v", err)
}
// Write mongodb error to file
if _, err := h.failoverFile.WriteString(err.Error() + "\n"); err != nil {
return fmt.Errorf("failed to save mongodb error to failoverfile: %v", err)
}
}
return fmt.Errorf("failed to save log to mongodb: %v", err)
}
return nil
}