Path: blob/main/component/otelcol/exporter/loki/internal/convert/convert_loki.go
4100 views
package convert12// This file is a near copy of3// https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.63.0/pkg/translator/loki/convert.go4//5// A copy was made because the upstream package contains some unexported6// definitions. If they're ever made public, our copy can be removed.7//8// Copyright The OpenTelemetry Authors9//10// Licensed under the Apache License, Version 2.0 (the "License");11// you may not use this file except in compliance with the License.12// You may obtain a copy of the License at13//14// http://www.apache.org/licenses/LICENSE-2.015//16// Unless required by applicable law or agreed to in writing, software17// distributed under the License is distributed on an "AS IS" BASIS,18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.19// See the License for the specific language governing permissions and20// limitations under the License.2122import (23"fmt"24"strings"25"time"2627"github.com/grafana/loki/pkg/logproto"28"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/loki"29"github.com/prometheus/common/model"30"go.opentelemetry.io/collector/pdata/pcommon"31"go.opentelemetry.io/collector/pdata/plog"32)3334const (35hintAttributes = "loki.attribute.labels"36hintResources = "loki.resource.labels"37hintTenant = "loki.tenant"38hintFormat = "loki.format"39levelAttributeName = "level"40)4142const (43formatJSON string = "json"44formatLogfmt string = "logfmt"45)4647var defaultExporterLabels = model.LabelSet{"exporter": "OTLP"}48var timeNow = time.Now4950func convertAttributesAndMerge(logAttrs pcommon.Map, resAttrs pcommon.Map) model.LabelSet {51out := defaultExporterLabels5253if resourcesToLabel, found := resAttrs.Get(hintResources); found {54labels := convertAttributesToLabels(resAttrs, resourcesToLabel)55out = out.Merge(labels)56}5758// get the hint from the log attributes, not from the resource59// the value can be a single resource name to use as label60// or a slice of string values61if resourcesToLabel, found := logAttrs.Get(hintResources); found {62labels := convertAttributesToLabels(resAttrs, resourcesToLabel)63out = out.Merge(labels)64}6566if attributesToLabel, found := logAttrs.Get(hintAttributes); found {67labels := convertAttributesToLabels(logAttrs, attributesToLabel)68out = out.Merge(labels)69}7071// get tenant hint from resource attributes, fallback to record attributes72// if it is not found73if resourcesToLabel, found := resAttrs.Get(hintTenant); !found {74if attributesToLabel, found := logAttrs.Get(hintTenant); found {75labels := convertAttributesToLabels(logAttrs, attributesToLabel)76out = out.Merge(labels)77}78} else {79labels := convertAttributesToLabels(resAttrs, resourcesToLabel)80out = out.Merge(labels)81}8283return out84}8586func convertAttributesToLabels(attributes pcommon.Map, attrsToSelect pcommon.Value) model.LabelSet {87out := model.LabelSet{}8889attrs := parseAttributeNames(attrsToSelect)90for _, attr := range attrs {91attr = strings.TrimSpace(attr)92av, ok := attributes.Get(attr) // do we need to trim this?93if ok {94out[model.LabelName(attr)] = model.LabelValue(av.AsString())95}96}9798return out99}100101func parseAttributeNames(attrsToSelect pcommon.Value) []string {102var out []string103104switch attrsToSelect.Type() {105case pcommon.ValueTypeStr:106out = strings.Split(attrsToSelect.AsString(), ",")107case pcommon.ValueTypeSlice:108as := attrsToSelect.Slice().AsRaw()109for _, a := range as {110out = append(out, fmt.Sprintf("%v", a))111}112default:113// trying to make the most of bad data114out = append(out, attrsToSelect.AsString())115}116117return out118}119120func removeAttributes(attrs pcommon.Map, labels model.LabelSet) {121attrs.RemoveIf(func(s string, v pcommon.Value) bool {122if s == hintAttributes || s == hintResources || s == hintTenant || s == hintFormat {123return true124}125126_, exists := labels[model.LabelName(s)]127return exists128})129}130131func convertLogToJSONEntry(lr plog.LogRecord, res pcommon.Resource) (*logproto.Entry, error) {132line, err := loki.Encode(lr, res)133if err != nil {134return nil, err135}136return &logproto.Entry{137Timestamp: timestampFromLogRecord(lr),138Line: line,139}, nil140}141142func convertLogToLogfmtEntry(lr plog.LogRecord, res pcommon.Resource) (*logproto.Entry, error) {143line, err := loki.EncodeLogfmt(lr, res)144if err != nil {145return nil, err146}147return &logproto.Entry{148Timestamp: timestampFromLogRecord(lr),149Line: line,150}, nil151}152153func convertLogToLokiEntry(lr plog.LogRecord, res pcommon.Resource, format string) (*logproto.Entry, error) {154switch format {155case formatJSON:156return convertLogToJSONEntry(lr, res)157case formatLogfmt:158return convertLogToLogfmtEntry(lr, res)159default:160return nil, fmt.Errorf("invalid format %s. Expected one of: %s, %s", format, formatJSON, formatLogfmt)161}162}163164func timestampFromLogRecord(lr plog.LogRecord) time.Time {165if lr.Timestamp() != 0 {166return time.Unix(0, int64(lr.Timestamp()))167}168169if lr.ObservedTimestamp() != 0 {170return time.Unix(0, int64(lr.ObservedTimestamp()))171}172173return time.Unix(0, int64(pcommon.NewTimestampFromTime(timeNow())))174}175176177