-
Notifications
You must be signed in to change notification settings - Fork 296
/
log_event.go
133 lines (109 loc) · 3.48 KB
/
log_event.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
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package newrelic
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
)
const (
// LogSeverityFieldName is the name of the log level field in New Relic logging JSON
LogSeverityFieldName = "level"
// LogMessageFieldName is the name of the log message field in New Relic logging JSON
LogMessageFieldName = "message"
// LogTimestampFieldName is the name of the timestamp field in New Relic logging JSON
LogTimestampFieldName = "timestamp"
// LogSpanIDFieldName is the name of the span ID field in the New Relic logging JSON
LogSpanIDFieldName = "span.id"
// LogTraceIDFieldName is the name of the trace ID field in the New Relic logging JSON
LogTraceIDFieldName = "trace.id"
// LogSeverityUnknown is the value the log severity should be set to if no log severity is known
LogSeverityUnknown = "UNKNOWN"
// MaxLogLength is the maximum number of bytes the log message is allowed to be
MaxLogLength = 32768
)
// for internal user only
type logEvent struct {
priority priority
timestamp int64
severity string
message string
spanID string
traceID string
}
// LogData contains data fields that are needed to generate log events.
type LogData struct {
Timestamp int64 // Required: Unix Millisecond Timestamp
Severity string // Optional: Severity of log being consumed
Message string // Optional: Message of log being consumed; Maximum size: 32768 Bytes.
Context context.Context // Optional: context containing a New Relic Transaction
}
// writeJSON prepares JSON in the format expected by the collector.
func (e *logEvent) WriteJSON(buf *bytes.Buffer) {
w := jsonFieldsWriter{buf: buf}
buf.WriteByte('{')
w.stringField(LogSeverityFieldName, e.severity)
w.stringField(LogMessageFieldName, e.message)
if len(e.spanID) > 0 {
w.stringField(LogSpanIDFieldName, e.spanID)
}
if len(e.traceID) > 0 {
w.stringField(LogTraceIDFieldName, e.traceID)
}
w.needsComma = false
buf.WriteByte(',')
w.intField(LogTimestampFieldName, e.timestamp)
buf.WriteByte('}')
}
// MarshalJSON is used for testing.
func (e *logEvent) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0, 256))
e.WriteJSON(buf)
return buf.Bytes(), nil
}
var (
// regex allows a single word, or number
severityUnknown = "UNKNOWN"
errEmptyTimestamp = errors.New("timestamp can not be empty")
errNilLogData = errors.New("log data can not be nil")
errLogMessageTooLarge = fmt.Errorf("log message can not exceed %d bytes", MaxLogLength)
)
func (data *LogData) toLogEvent() (*logEvent, error) {
if data == nil {
return nil, errNilLogData
}
if data.Severity == "" {
data.Severity = LogSeverityUnknown
}
if len(data.Message) > MaxLogLength {
return nil, errLogMessageTooLarge
}
if data.Timestamp == 0 {
return nil, errEmptyTimestamp
}
data.Message = strings.TrimSpace(data.Message)
data.Severity = strings.TrimSpace(data.Severity)
var spanID, traceID string
var priority priority
if data.Context != nil {
txn := FromContext(data.Context)
priority = txn.thread.BetterCAT.Priority
traceMetadata := txn.GetTraceMetadata()
spanID = traceMetadata.SpanID
traceID = traceMetadata.TraceID
}
event := logEvent{
message: data.Message,
severity: data.Severity,
spanID: spanID,
traceID: traceID,
timestamp: data.Timestamp,
priority: priority,
}
return &event, nil
}
func (e *logEvent) MergeIntoHarvest(h *harvest) {
h.LogEvents.Add(e)
}