/
gcp_logadmin.go
117 lines (110 loc) · 3.41 KB
/
gcp_logadmin.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
package bot
import (
"context"
"fmt"
"regexp"
"strings"
"time"
"cloud.google.com/go/logging"
"cloud.google.com/go/logging/logadmin"
"github.com/golang/protobuf/ptypes/struct"
"google.golang.org/api/iterator"
)
type GCPLogAdmin struct {
ProjectID string
LogName string
Timezone string
}
func (g *GCPLogAdmin) GetLogAdminFilter(payload IncidentPayload) string {
return fmt.Sprintf(`logName = "projects/%s/logs/%s" AND timestamp >= %q AND timestamp <= %q`,
g.ProjectID,
g.LogName,
payload.Incident.StartedAtTime(),
payload.Incident.EndedAtTime())
}
func (g *GCPLogAdmin) GetLogEntries(payload IncidentPayload) ([]IncidentLogEntry, error) {
incidentEntries := []IncidentLogEntry{}
ctx := context.Background()
adminClient, err := logadmin.NewClient(ctx, g.ProjectID)
if err != nil {
return incidentEntries, fmt.Errorf("Failed to create logadmin client: %v\n", err)
}
var logEntries []*logging.Entry
iter := adminClient.Entries(ctx,
logadmin.Filter(g.GetLogAdminFilter(payload)),
logadmin.NewestFirst(),
)
retrievalCount := 0
for {
entry, err := iter.Next()
retrievalCount = retrievalCount + 1
if err == iterator.Done {
break
}
if retrievalCount >= 50 {
break
}
if err != nil {
fmt.Printf("Failed to get next log entry: %v\n", err)
continue
}
logEntries = append(logEntries, entry)
}
for _, entry := range logEntries {
if entry == nil {
continue
}
entryPayload := entry.Payload
if value, ok := entryPayload.(*structpb.Struct); ok {
if value == nil {
continue
}
newIncidentEntry := IncidentLogEntry{}
newIncidentEntry.Environment = payloadFieldToString(*value, "environment")
newIncidentEntry.Description = payloadFieldToString(*value, "message")
newIncidentEntry.Error = payloadFieldToString(*value, "error")
newIncidentEntry.Caller = payloadFieldToString(*value, "caller")
eventTimeString := payloadFieldToString(*value, "eventTime")
eventTime, err := timeStringToZone(eventTimeString, g.Timezone)
if err != nil {
fmt.Printf("Failed to parse date %s from log entry: %v\n", eventTimeString, err)
continue
}
newIncidentEntry.EventTime = eventTime
serviceContext := value.Fields["serviceContext"]
if serviceContext != nil {
contextStruct := *serviceContext.GetStructValue()
newIncidentEntry.Service = contextStruct.Fields["service"].GetStringValue()
}
verboseResourceName := payload.Incident.ResourceName
if strings.Contains(verboseResourceName, "Amazon EC2 Instance labels") {
r, _ := regexp.Compile(`instance_id=(\S*)`)
result := r.FindStringSubmatch(verboseResourceName)
if len(result) >= 2 {
newIncidentEntry.ResourceID = result[1]
}
}
incidentEntries = append(incidentEntries, newIncidentEntry)
}
}
return incidentEntries, nil
}
func payloadFieldToString(payload structpb.Struct, fieldName string) string {
stringValue := ""
if payload.Fields[fieldName] != nil {
stringValue = (*payload.Fields[fieldName]).GetStringValue()
}
return stringValue
}
func timeStringToZone(timeString string, locationString string) (time.Time, error) {
location, err := time.LoadLocation(locationString)
if err != nil {
return time.Now(), fmt.Errorf("Failed to parse timezone location for log entry: %v\n", err)
}
eventTime, err := time.Parse(time.RFC3339, timeString)
if err != nil {
fmt.Printf("Failed to parse time from log entry: %v\n", err)
return time.Now(), err
}
return eventTime.In(location), nil
}