Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/component/otelcol/exporter/loki/internal/convert/convert_loki.go
4100 views
1
package convert
2
3
// This file is a near copy of
4
// https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.63.0/pkg/translator/loki/convert.go
5
//
6
// A copy was made because the upstream package contains some unexported
7
// definitions. If they're ever made public, our copy can be removed.
8
//
9
// Copyright The OpenTelemetry Authors
10
//
11
// Licensed under the Apache License, Version 2.0 (the "License");
12
// you may not use this file except in compliance with the License.
13
// You may obtain a copy of the License at
14
//
15
// http://www.apache.org/licenses/LICENSE-2.0
16
//
17
// Unless required by applicable law or agreed to in writing, software
18
// distributed under the License is distributed on an "AS IS" BASIS,
19
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
// See the License for the specific language governing permissions and
21
// limitations under the License.
22
23
import (
24
"fmt"
25
"strings"
26
"time"
27
28
"github.com/grafana/loki/pkg/logproto"
29
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/loki"
30
"github.com/prometheus/common/model"
31
"go.opentelemetry.io/collector/pdata/pcommon"
32
"go.opentelemetry.io/collector/pdata/plog"
33
)
34
35
const (
36
hintAttributes = "loki.attribute.labels"
37
hintResources = "loki.resource.labels"
38
hintTenant = "loki.tenant"
39
hintFormat = "loki.format"
40
levelAttributeName = "level"
41
)
42
43
const (
44
formatJSON string = "json"
45
formatLogfmt string = "logfmt"
46
)
47
48
var defaultExporterLabels = model.LabelSet{"exporter": "OTLP"}
49
var timeNow = time.Now
50
51
func convertAttributesAndMerge(logAttrs pcommon.Map, resAttrs pcommon.Map) model.LabelSet {
52
out := defaultExporterLabels
53
54
if resourcesToLabel, found := resAttrs.Get(hintResources); found {
55
labels := convertAttributesToLabels(resAttrs, resourcesToLabel)
56
out = out.Merge(labels)
57
}
58
59
// get the hint from the log attributes, not from the resource
60
// the value can be a single resource name to use as label
61
// or a slice of string values
62
if resourcesToLabel, found := logAttrs.Get(hintResources); found {
63
labels := convertAttributesToLabels(resAttrs, resourcesToLabel)
64
out = out.Merge(labels)
65
}
66
67
if attributesToLabel, found := logAttrs.Get(hintAttributes); found {
68
labels := convertAttributesToLabels(logAttrs, attributesToLabel)
69
out = out.Merge(labels)
70
}
71
72
// get tenant hint from resource attributes, fallback to record attributes
73
// if it is not found
74
if resourcesToLabel, found := resAttrs.Get(hintTenant); !found {
75
if attributesToLabel, found := logAttrs.Get(hintTenant); found {
76
labels := convertAttributesToLabels(logAttrs, attributesToLabel)
77
out = out.Merge(labels)
78
}
79
} else {
80
labels := convertAttributesToLabels(resAttrs, resourcesToLabel)
81
out = out.Merge(labels)
82
}
83
84
return out
85
}
86
87
func convertAttributesToLabels(attributes pcommon.Map, attrsToSelect pcommon.Value) model.LabelSet {
88
out := model.LabelSet{}
89
90
attrs := parseAttributeNames(attrsToSelect)
91
for _, attr := range attrs {
92
attr = strings.TrimSpace(attr)
93
av, ok := attributes.Get(attr) // do we need to trim this?
94
if ok {
95
out[model.LabelName(attr)] = model.LabelValue(av.AsString())
96
}
97
}
98
99
return out
100
}
101
102
func parseAttributeNames(attrsToSelect pcommon.Value) []string {
103
var out []string
104
105
switch attrsToSelect.Type() {
106
case pcommon.ValueTypeStr:
107
out = strings.Split(attrsToSelect.AsString(), ",")
108
case pcommon.ValueTypeSlice:
109
as := attrsToSelect.Slice().AsRaw()
110
for _, a := range as {
111
out = append(out, fmt.Sprintf("%v", a))
112
}
113
default:
114
// trying to make the most of bad data
115
out = append(out, attrsToSelect.AsString())
116
}
117
118
return out
119
}
120
121
func removeAttributes(attrs pcommon.Map, labels model.LabelSet) {
122
attrs.RemoveIf(func(s string, v pcommon.Value) bool {
123
if s == hintAttributes || s == hintResources || s == hintTenant || s == hintFormat {
124
return true
125
}
126
127
_, exists := labels[model.LabelName(s)]
128
return exists
129
})
130
}
131
132
func convertLogToJSONEntry(lr plog.LogRecord, res pcommon.Resource) (*logproto.Entry, error) {
133
line, err := loki.Encode(lr, res)
134
if err != nil {
135
return nil, err
136
}
137
return &logproto.Entry{
138
Timestamp: timestampFromLogRecord(lr),
139
Line: line,
140
}, nil
141
}
142
143
func convertLogToLogfmtEntry(lr plog.LogRecord, res pcommon.Resource) (*logproto.Entry, error) {
144
line, err := loki.EncodeLogfmt(lr, res)
145
if err != nil {
146
return nil, err
147
}
148
return &logproto.Entry{
149
Timestamp: timestampFromLogRecord(lr),
150
Line: line,
151
}, nil
152
}
153
154
func convertLogToLokiEntry(lr plog.LogRecord, res pcommon.Resource, format string) (*logproto.Entry, error) {
155
switch format {
156
case formatJSON:
157
return convertLogToJSONEntry(lr, res)
158
case formatLogfmt:
159
return convertLogToLogfmtEntry(lr, res)
160
default:
161
return nil, fmt.Errorf("invalid format %s. Expected one of: %s, %s", format, formatJSON, formatLogfmt)
162
}
163
}
164
165
func timestampFromLogRecord(lr plog.LogRecord) time.Time {
166
if lr.Timestamp() != 0 {
167
return time.Unix(0, int64(lr.Timestamp()))
168
}
169
170
if lr.ObservedTimestamp() != 0 {
171
return time.Unix(0, int64(lr.ObservedTimestamp()))
172
}
173
174
return time.Unix(0, int64(pcommon.NewTimestampFromTime(timeNow())))
175
}
176
177