Skip to content

Commit 38d939b

Browse files
authored
Merge pull request #596 from newrelic/develop
Go Agent 3.20.0. Release
2 parents c611087 + d947486 commit 38d939b

File tree

15 files changed

+889
-107
lines changed

15 files changed

+889
-107
lines changed

.github/workflows/ci.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ jobs:
5151
- go-version: 1.13.x
5252
dirs: _integrations/nrecho
5353
pin: github.com/labstack/[email protected]
54-
- go-version: 1.13.x
55-
dirs: _integrations/nrgin/v1
5654
- go-version: 1.13.x
5755
dirs: _integrations/nrgorilla/v1
5856
- go-version: 1.13.x

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1+
## 3.20.0
2+
3+
**PLEASE READ** these changes, and verify your config settings to ensure your application behaves how you intend it to. This release changes some default behaviors in the go agent.
4+
5+
### Added
6+
* The Module Dependency Metrics feature was added. This collects the list of modules imported into your application, to aid in management of your application dependencies, enabling easier vulnerability detection and response, etc.
7+
* This feature is enabled by default, but may be disabled by explicitly including `ConfigModuleDependencyMetricsEnable(false)` in your application, or setting the equivalent environment variable or `Config` field direclty.
8+
* Modules may be explicitly excluded from the report via the `ConfigModuleDependencyMetricsIgnoredPrefixes` option.
9+
* Excluded module names may be redacted via the `ConfigModuleDependencyMetricsRedactIgnoredPrefixes` option. This is enabled by default.
10+
* Application Log Forwarding will now be **ENABLED** by default
11+
* Automatic application log forwarding is now enabled by default. This means that logging frameworks wrapped with one of the [logcontext-v2 integrations](https://docs.newrelic.com/docs/apm/agents/go-agent/get-started/go-agent-compatibility-requirements/) will automatically send enriched application logs to New Relic with this version of the agent. To learn more about this feature, see the [APM logs in context documentation](https://docs.newrelic.com/docs/logs/logs-context/logs-in-context/). For additional configuration options, see the [Go logs in context documentation](https://docs.newrelic.com/docs/logs/logs-context/configure-logs-context-go). To learn about how to toggle log ingestion on or off by account, see our documentation to [disable automatic](https://docs.newrelic.com/docs/logs/logs-context/disable-automatic-logging) logging via the UI or API.
12+
* If you are using a logcontext-v2 extension, but don't want the agent to automatically forward logs, please configure `ConfigAppLogForwardingEnabled(false)` in your application.
13+
* Environment variables have been added for all application logging config options:
14+
* `NEW_RELIC_APPLICATION_LOGGING_ENABLED`
15+
* `NEW_RELIC_APPLICATION_LOGGING_FORWARDING_ENABLED`
16+
* `NEW_RELIC_APPLICATION_LOGGING_FORWARDING_MAX_SAMPLES_STORED`
17+
* `NEW_RELIC_APPLICATION_LOGGING_METRICS_ENABLED`
18+
* `NEW_RELIC_APPLICATION_LOGGING_LOCAL_DECORATING_ENABLED`
19+
* Custom Event Limit Increase
20+
* This version increases the **DEFAULT** limit of custom events from 10,000 events per minute to 30,000 events per minute. In the scenario that custom events were being limited, this change will allow more custom events to be sent to New Relic. There is also a new configurable **MAXIMUM** limit of 100,000 events per minute. To change the limits, set `ConfigCustomInsightsEventsMaxSamplesStored(limit)` to the limit you want in your application. To learn more about the change and how to determine if custom events are being dropped, see our Explorers Hub [post](https://discuss.newrelic.com/t/send-more-custom-events-with-the-latest-apm-agents/190497).
21+
* New config option `ConfigCustomInsightsEventsEnabled(false)` can be used to disable the collection of custom events in your application.
22+
23+
### Changed
24+
* Changed the following names to be consistent with their usage and other related identifier names. The old names remain for backward compatibility, but new code should use the new names.
25+
* `ConfigCodeLevelMetricsIgnoredPrefix` -> `ConfigCodeLevelMetricsIgnoredPrefixes`
26+
* `ConfigCodeLevelMetricsPathPrefix` -> `ConfigCodeLevelMetricsPathPrefixes`
27+
* `NEW_RELIC_CODE_LEVEL_METRICS_PATH_PREFIX` -> `NEW_RELIC_CODE_LEVEL_METRICS_PATH_PREFIXES`
28+
* `NEW_RELIC_CODE_LEVEL_METRICS_IGNORED_PREFIX` -> `NEW_RELIC_CODE_LEVEL_METRICS_IGNORED_PREFIXES`
29+
30+
* When excluding information reported from CodeLevelMetrics via the `IgnoredPrefixes` or `PathPrefixes` configuration fields (e.g., by specifying `ConfigCodeLevelMetricsIgnoredPrefixes` or `ConfigCodeLevelMetricsPathPrefixes`), the names of the ignored prefixes and the configured path prefixes may now be redacted from the agent configuration information sent to New Relic.
31+
* This redaction is enabled by default, but may be disabled by supplying a `false` value to `ConfigCodeLevelMetricsRedactPathPrefixes` or `ConfigCodeLevelMetricsRedactIgnoredPrefixes`, or by setting the corresponding `Config` fields or environment variables to `false`.
32+
33+
### Fixed
34+
* [#583](https://github.com/newrelic/go-agent/issues/583): fixed a bug in zerologWriter where comma separated fields in log message confused the JSON parser and could cause panics.
35+
36+
### Support Statement
37+
New Relic recommends that you upgrade the agent regularly to ensure that you’re getting the latest features and performance benefits. Additionally, older releases will no longer be supported when they reach end-of-life.
38+
39+
We also recommend using the latest version of the Go language. At minimum, you should at least be using no version of Go older than what is supported by the Go team themselves.
40+
41+
See the [Go Agent EOL Policy](https://docs.newrelic.com/docs/apm/agents/go-agent/get-started/go-agent-eol-policy/) for details about supported versions of the Go Agent and third-party components.
42+
143
## 3.19.2
244

345
### Changed

v3/integrations/logcontext-v2/zerologWriter/zerolog-writer.go

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"io"
66
"strings"
7+
"time"
8+
"unicode"
79

810
"github.com/newrelic/go-agent/v3/integrations/logcontext-v2/nrwriter"
911
"github.com/newrelic/go-agent/v3/internal"
@@ -51,22 +53,33 @@ func parseJSONLogData(log []byte) newrelic.LogData {
5153
// For this iteration of the tool, the entire log gets captured as the message
5254
data := newrelic.LogData{}
5355
data.Message = string(log)
56+
data.Timestamp = time.Now().UnixMilli()
5457

5558
for i := 0; i < len(log)-1; {
5659
// get key; always a string field
57-
key, keyEnd := getStringField(log, i)
58-
59-
// find index where value starts
60-
valStart := getValueIndex(log, keyEnd)
61-
valEnd := valStart
60+
key, valStart := getKey(log, i)
61+
var next int
6262

6363
// NOTE: depending on the key, the type of field the value is can differ
6464
switch key {
6565
case zerolog.LevelFieldName:
66-
data.Severity, valEnd = getStringField(log, valStart)
66+
data.Severity, next = getStringValue(log, valStart+1)
67+
case zerolog.ErrorStackFieldName:
68+
_, next = getStackTrace(log, valStart)
69+
default:
70+
if i >= len(log)-1 {
71+
return data
72+
}
73+
// TODO: once we update the logging spec to support custom attributes, capture these
74+
if isStringValue(log, valStart) {
75+
_, next = getStringValue(log, valStart+1)
76+
} else if isNumberValue(log, valStart) {
77+
_, next = getNumberValue(log, valStart)
78+
} else {
79+
return data
80+
}
6781
}
6882

69-
next := nextKeyIndex(log, valEnd)
7083
if next == -1 {
7184
return data
7285
}
@@ -76,49 +89,98 @@ func parseJSONLogData(log []byte) newrelic.LogData {
7689
return data
7790
}
7891

79-
func getValueIndex(p []byte, indx int) int {
80-
// Find the index where the value begins
81-
for i := indx; i < len(p)-1; i++ {
82-
if p[i] == ':' {
83-
return i + 1
84-
}
85-
}
86-
87-
return -1
92+
func isStringValue(p []byte, indx int) bool {
93+
return p[indx] == '"'
8894
}
8995

90-
func nextKeyIndex(p []byte, indx int) int {
91-
// Find the index where the key begins
92-
for i := indx; i < len(p)-1; i++ {
93-
if p[i] == ',' {
94-
return i + 1
95-
}
96-
}
97-
98-
return -1
96+
func isNumberValue(p []byte, indx int) bool {
97+
return unicode.IsDigit(rune(p[indx]))
9998
}
10099

101-
func getStringField(p []byte, indx int) (string, int) {
100+
// zerolog keys are always JSON strings
101+
func getKey(p []byte, indx int) (string, int) {
102102
value := strings.Builder{}
103103
i := indx
104104

105105
// find start of string field
106-
for ; i < len(p)-1; i++ {
106+
for ; i < len(p); i++ {
107107
if p[i] == '"' {
108108
i += 1
109109
break
110110
}
111111
}
112112

113113
// parse value of string field
114-
for ; i < len(p)-1; i++ {
115-
if p[i] == '"' {
116-
return value.String(), i + 1
114+
for ; i < len(p); i++ {
115+
if p[i] == '"' && i+1 < len(p) && p[i+1] == ':' {
116+
return value.String(), i + 2
117117
} else {
118118
value.WriteByte(p[i])
119119
}
120+
}
121+
122+
return "", -1
123+
}
124+
125+
func isEOL(p []byte, i int) bool {
126+
return p[i] == '}' && i+2 == len(p)
127+
}
128+
129+
func getStringValue(p []byte, indx int) (string, int) {
130+
value := strings.Builder{}
120131

132+
// parse value of string field
133+
for i := indx; i < len(p); i++ {
134+
if p[i] == '"' && i+1 < len(p) {
135+
if p[i+1] == ',' && i+2 < len(p) && p[i+2] == '"' {
136+
return value.String(), i + 2
137+
} else if isEOL(p, i+1) {
138+
return value.String(), -1
139+
}
140+
}
141+
value.WriteByte(p[i])
121142
}
122143

123144
return "", -1
124145
}
146+
147+
func getNumberValue(p []byte, indx int) (string, int) {
148+
value := strings.Builder{}
149+
150+
// parse value of string field
151+
for i := indx; i < len(p); i++ {
152+
if p[i] == ',' && i+1 < len(p) && p[i+1] == '"' {
153+
return value.String(), i + 1
154+
} else if isEOL(p, i) {
155+
return value.String(), -1
156+
} else {
157+
value.WriteByte(p[i])
158+
}
159+
}
160+
161+
return "", -1
162+
}
163+
164+
func getStackTrace(p []byte, indx int) (string, int) {
165+
value := strings.Builder{}
166+
167+
// parse value of string field
168+
for i := indx; i < len(p); i++ {
169+
if p[i] == ']' {
170+
value.WriteByte(p[i])
171+
172+
if i+1 < len(p) {
173+
if isEOL(p, i+1) {
174+
return value.String(), -1
175+
}
176+
if p[i+1] == ',' && i+2 < len(p) && p[i+2] == '"' {
177+
return value.String(), i + 2
178+
}
179+
}
180+
} else {
181+
value.WriteByte(p[i])
182+
}
183+
}
184+
185+
return value.String(), -1
186+
}

0 commit comments

Comments
 (0)